Re: [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names
On Wed, Aug 8, 2018 at 6:50 PM Jeff King wrote: > On Tue, Aug 07, 2018 at 04:21:31AM -0400, Eric Sunshine wrote: > > +# Swallowing here-docs with arbitrary tags requires a bit of finesse. When > > a > > +# line such as "cat> front > > +# of the line enclosed in angle brackets as a sentinel, giving "cat > > >out". > > Gross, but OK, as long as we would not get confused by a line that > actually started with at the start. It can't get confused by such a line. There here-doc swallower prepends that when it starts the swallowing process and removes it add the end. Even if a line actually started with that, it would become "cmd" while swallowing the here-doc, and be restored to "cmd" at the end. Stripping the "" is done non-greedily, so it wouldn't remove both of them. Likewise, non-greedy matching is used for pulling the "EOF" out of the "<...>" when trying to match against the terminating "EOF" line, so there can be no confusion. > > +/<<[ ]*[-\\]*[A-Z0-9_][A-Z0-9_]*/ { > > + s/^\(.*\)<<[]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1< > + s/[ ]*< > Here-docs can use lowercase, too, though I'd personally frown on that > from a style perspective. Yeah, I was going with the tighter uppercase-only which Jonathan suggested[1], but I guess it wouldn't hurt to re-roll to allow lowercase too. [1]: https://public-inbox.org/git/20180730205914.ge156...@aiede.svl.corp.google.com/ > It looks like this doesn't catch: > > cat <<'EOF' > EOF > > either. I think we prefer the backslash style, but there are quite a few > <<-'EOF' hits. Is it covered somewhere else? No. I've gotten so used to \EOF in this codebase that it didn't occur to me to even think about 'EOF', but a re-roll could add that, as well.
Re: Help with "fatal: unable to read ...." error during GC?
On Wed, 2018-08-08 at 14:24 -0400, Jeff King wrote: > If so, can you try running it under gdb and getting a stack trace? > Something like: > > gdb git > [and then inside gdb...] > set args pack-objects --all --reflog --indexed-objects foobreak die > run > bt > > That might give us a clue where the broken object reference is coming Here we go. I can rebuild with -Og or -O0 if more detailed debugging is needed; most everything appears to be optimized out: ... Compressing objects: 100% (10/10), done. Writing objects: 54% (274416/508176) Thread 1 "git" hit Breakpoint 1, die (err=err@entry=0x5a373a "unable to read %s") at usage.c:119 119 { (gdb) bt #0 die (err=err@entry=0x5a373a "unable to read %s") at usage.c:119 #1 0x004563f3 in get_delta (entry=) at builtin/pack-objects.c:143 #2 write_no_reuse_object () at builtin/pack-objects.c:308 #3 0x00456592 in write_reuse_object (usable_delta=, limit=, entry=, f=) at builtin/pack-objects.c:516 #4 write_object (write_offset=, entry=0x7fffc9a8d940, f=0x198fb70) at builtin/pack-objects.c:518 #5 write_one () at builtin/pack-objects.c:576 #6 0x004592f0 in write_pack_file () at builtin/pack-objects.c:849 #7 cmd_pack_objects (argc=, argv=, prefix=) at builtin/pack-objects.c:3354 #8 0x00404f06 in run_builtin (argv=, argc=, p=) at git.c:417 #9 handle_builtin (argc=, argv=) at git.c:632 #10 0x00405f21 in run_argv (argv=0x7fffe210, argcp=0x7fffe21c) at git.c:761 #11 cmd_main (argc=, argc@entry=6, argv=, argv@entry=0x7fffe448) at git.c:761 #12 0x00404b15 in main (argc=6, argv=0x7fffe448) at common-main.c:45
Re: [PATCH 1/1] verify-tag/verify-commit should exit unsuccessfully when signature is not trusted
On Wed, Aug 08, 2018 at 05:59:43PM -0700, Junio C Hamano wrote: > "brian m. carlson" writes: > > >> FWIW, I'm on board with returning non-zero in any case where gpg would. > > > > I think that's probably the best solution overall. > > FWIW, I am not married to the current behaviour. I would not be > surprised if it mostly came by accident and not designed. Since apparently I was the author of the commit that changed the behavior originally, let me simply say that I was not aware that gpg signalled the correctness of a signature by its exit status when I wrote that patch. If I had known that, I would have deferred to gpg in my change. My goal was consistency between verify-tag and verify-commit, and in retrospect I probably made the wrong decision. > > There's a bug report > > in Debian (https://bugs.debian.org/895048) that requests that behavior > > instead of the status quo, and also it's the behavior that's documented: > > The last bit is a bit questionable; I think you are reading too much > into the description. > > A substitute for gpg.program MUST signal good (or not good) > signature the same way as gpg would with its exit code---that is all > the description says. It does not say anything about how that exit > code affects the exit status of "tag --verify" and friends that > called gpg.program. I agree that the description doesn't specifically say that. In fact, it doesn't explicitly say that it must exit nonzero on a bad signature, although I agree with your interpretation that that would be logical (and, AIUI, the behavior of gpg). However, I would assert that we do want Git's verification to exit successfully on a good signature and unsuccessfully on a bad signature, and deferring to gpg may be the most robust way of ensuring that. -- brian m. carlson: Houston, Texas, US OpenPGP: https://keybase.io/bk2204 signature.asc Description: PGP signature
Re: [PATCH 1/1] verify-tag/verify-commit should exit unsuccessfully when signature is not trusted
"brian m. carlson" writes: >> FWIW, I'm on board with returning non-zero in any case where gpg would. > > I think that's probably the best solution overall. FWIW, I am not married to the current behaviour. I would not be surprised if it mostly came by accident and not designed. > There's a bug report > in Debian (https://bugs.debian.org/895048) that requests that behavior > instead of the status quo, and also it's the behavior that's documented: The last bit is a bit questionable; I think you are reading too much into the description. A substitute for gpg.program MUST signal good (or not good) signature the same way as gpg would with its exit code---that is all the description says. It does not say anything about how that exit code affects the exit status of "tag --verify" and friends that called gpg.program. >gpg.program >Use this custom program instead of "gpg" found on $PATH when >making or verifying a PGP signature. The program must support >the same command-line interface as GPG, namely, to verify a >detached signature, "gpg --verify $file - <$signature" is >run, and the program is expected to signal a good signature >by exiting with code 0, and to generate an ASCII-armored >detached signature, the standard input of "gpg -bsau $key" is >fed with the contents to be signed, and the program is >expected to send the result to its standard output.
Re: [PATCH 1/2] submodule: create helper to build paths to submodule gitdirs
On 08/08, Stefan Beller wrote: > On Wed, Aug 8, 2018 at 3:33 PM Brandon Williams wrote: > > > > Introduce a helper function "submodule_name_to_gitdir()" (and the > > submodule--helper subcommand "gitdir") which constructs a path to a > > submodule's gitdir, located in the provided repository's "modules" > > directory. > > Makes sense. > > > > > This consolidates the logic needed to build up a path into a > > repository's "modules" directory, abstracting away the fact that > > submodule git directories are stored in a repository's common gitdir. > > This makes it easier to adjust how submodules gitdir are stored in the > > "modules" directory in a future patch. > > and yet, all places that we touch were and still are broken for old-style > submodules that have their git directory inside the working tree? > Do we need to pay attention to those, too? This series only tries to address the issues with submodules stored in $GITDIR/modules/ and places in our codebase that explicitly reference submodules stored there. For those old-old-style submodules, wouldn't the absorb submodule functions handle that migration? > > > > diff --git a/git-submodule.sh b/git-submodule.sh > > index 8b5ad59bde..053747d290 100755 > > --- a/git-submodule.sh > > +++ b/git-submodule.sh > > > @@ -577,7 +578,7 @@ cmd_update() > > die "$(eval_gettext "Unable to find current > > \${remote_name}/\${branch} revision in submodule path '\$sm_path'")" > > fi > > > > - if ! $(git config -f "$(git rev-parse > > --git-common-dir)/modules/$name/config" core.worktree) 2>/dev/null > > + if ! $(git config -f "$(git submodule--helper gitdir > > "$name")/config" core.worktree) 2>/dev/null > > This will collide with origin/sb/submodule-update-in-c specifically > 1c866b9831d (submodule--helper: replace connect-gitdir-workingtree > by ensure-core-worktree, 2018-08-03), but as that removes these lines, > it should be easy to resolve the conflict. -- Brandon Williams
Re: [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding
Jeff King writes: > I had two minor comments on the first patch. I'll admit my eyes glazed > over looking at the rest of them, and to make any kind of intelligent > review I'd need to spend an hour understanding how the sed script works. > Which frankly, I'm not sure is worth it. Didn't I make this prediction when we started the "text inspection" approach that it quickly go downhill resulting in unmaintainable mess rather quickly ;-)? > Given the empirical results > (both on the real code base and the new tests you add) and the low-risk > nature (it's linting our tests, after all, not code users run), I'd be > inclined to say it's not making anything worse, and probably making > things better. We can find out about any further short-comings in the > wild. Amen to that.
Re: [PATCH v2] clone: report duplicate entries on case-insensitive filesystems
Jeff King writes: > I think we really want to avoid doing that normalization ourselves if we > can. There are just too many filesystem-specific rules. Exactly; not having to learn these rules is the major (if not whole) point of the "let checkout notice the collision and then deal with it" approach. Let's not forget that. > If we have an equivalence-class hashmap and feed it inodes (or again, > some system equivalent) as the keys, we should get buckets of > collisions. I guess one way to get "some system equivalent" that can be used as the last resort, when there absolutely is no inum equivalent, is to rehash the working tree file that shouldn't be there when we detect a collision. If we found that there is something when we tried to write out "Foo.txt", if we open "Foo.txt" on the working tree and hash-object it, we should find the matching blob somewhere in the index _before_ "Foo.txt". On a case-insensitive filesytem, it may well be "foo.txt", but we do not even have to know "foo.txt" and "Foo.txt" only differ in case. Of course, that's really the last resort, as it would be costly, but this is something that only need to happen on the "unusual" case in the error codepath, so...
Re: [RFC PATCH] packfile: iterate packed objects in pack order
On Wed, Aug 08, 2018 at 04:12:10PM -0700, Jonathan Tan wrote: > Many invocations of for_each_object_in_pack() and > for_each_packed_object() (which invokes the former) subsequently check > at least the type of the packed object, necessitating accessing the > packfile itself. For locality reasons, it is thus better to iterate in > pack order, instead of index order. Teach for_each_object_in_pack() to > iterate in pack order by first creating a reverse index. > > This is based off work by Jeff King. > > Signed-off-by: Jonathan Tan > --- > After writing this patch and looking at it further, I'm not sure if this > is a clear benefit, but here's the patch anyway. In particular, > builtin/fsck.c and builtin/cat-file.c just deal with the OID directly > and does not access the packfile at all (at least at the time of > invoking for_each_packed_object). And revision.c, if we are excluding > promisor objects, parses each packed promisor object, but it seems > possible to avoid doing that (replacing the parse_object() by > lookup_unknown_object() still passes tests). Even if you just use the oid to do a separate lookup in the object database, there's still a benefit in accessing the objects in pack order. The case in cat-file needs more than this, though, since it separately sorts the output (it has to, because it has to merge and de-dup the output from several packs plus loose objects). With the patch below on top of yours, I get: $ time git.v2.18.0 cat-file --batch-all-objects --buffer --batch | wc -c 6938365964 real 0m44.686s user 0m42.932s sys 0m5.283s $ time git.compile cat-file --batch-all-objects --buffer --batch | wc -c 8289859070 real 0m7.007s user 0m5.542s sys 0m4.005s But: - it needs to de-duplicate using a hashmap (which is why the output is so much bigger in the second case) - it probably needs to be enabled explicitly by the user, since cat-file is plumbing and callers may be relying on the existing sort order I can try to pick this up and carry the cat-file bits to completion if you want, but probably not until tomorrow or Friday. -Peff
Re: [PATCH 1/2] submodule: create helper to build paths to submodule gitdirs
On Wed, Aug 8, 2018 at 3:33 PM Brandon Williams wrote: > > Introduce a helper function "submodule_name_to_gitdir()" (and the > submodule--helper subcommand "gitdir") which constructs a path to a > submodule's gitdir, located in the provided repository's "modules" > directory. Makes sense. > > This consolidates the logic needed to build up a path into a > repository's "modules" directory, abstracting away the fact that > submodule git directories are stored in a repository's common gitdir. > This makes it easier to adjust how submodules gitdir are stored in the > "modules" directory in a future patch. and yet, all places that we touch were and still are broken for old-style submodules that have their git directory inside the working tree? Do we need to pay attention to those, too? > diff --git a/git-submodule.sh b/git-submodule.sh > index 8b5ad59bde..053747d290 100755 > --- a/git-submodule.sh > +++ b/git-submodule.sh > @@ -577,7 +578,7 @@ cmd_update() > die "$(eval_gettext "Unable to find current > \${remote_name}/\${branch} revision in submodule path '\$sm_path'")" > fi > > - if ! $(git config -f "$(git rev-parse > --git-common-dir)/modules/$name/config" core.worktree) 2>/dev/null > + if ! $(git config -f "$(git submodule--helper gitdir > "$name")/config" core.worktree) 2>/dev/null This will collide with origin/sb/submodule-update-in-c specifically 1c866b9831d (submodule--helper: replace connect-gitdir-workingtree by ensure-core-worktree, 2018-08-03), but as that removes these lines, it should be easy to resolve the conflict.
Re: abstracting commit signing/verify to support other signing schemes
On Mon, Aug 06, 2018 at 08:24:25PM +, Tacitus Aedifex wrote: > the older patch set suggested the idea of using PEM strings to match up the > signature payload with a certain signing tool. i can't tell if they mean > the 'pre-ecapsulation boundary' (e.g. '-BEGIN FOO-') or if they mean > the encapsulated headers; both as defined in RFC 1421 [0]. It was the pre-encapsulation boundary (we didn't use that word, but it was definitely the "-BEGIN" line ;) ). And that was the sticking point: there was an open question of what support for something like signify would look like exactly, and what the matching rules would need to be. My thought was to allow multiple matching types, and "PEM type" (by which I meant that pre-encapsulation boundary) would be the first such type. But that got punted on, since we didn't have a real-world example to look at, and we really only cared about gpgsm in the near-term anyway. And that obviously does PEM. So the gpg.* tools all require PEM, but if we add a generic signingtool config, it doesn't have to. > the newer patch set looks specifically at the pre-encapsulation boundary to > switch behaviors. that works assuming that the signing tools all understand > PEM. in the case of signify, it doesn't, so the driver code in git will have > to translate to/from PEM. Right. It might be fine to encapsulate it, though I prefer not inventing new micro-formats if we can avoid it. There actually _is_ an interesting micro-format already in Git, which is that commit signatures (not tags) are held in the "gpgsig" header of the commit. Which would be an awkward name for storing a non-gpg tool. We may want to live with that for historical reasons, or we could switch to a more generic name. The actual PEM detection is for tags, where the signature is in the tag body itself. In either case, we could use some object header to indicate the signature type (on the other hand, it could be possible to have multiple signatures of different types). > i suggest that we switch to a standard format for all signatures that is > similar to the commit message format with colon ':' separated fields > followed by a payload. the colon separated fields would specify the signing > tool used to generate the signature and the tool specific data followed by > the signature blob like so: > > signing-tool: gpg > gpg-keyid: 0123456789ABCDEF > -BEGIN PGP SIGNATURE- > > -END PGP SIGNATURE- > > by adopting this format, git will be fully abstracted from the underlying > signing tool and the user can specify multiple signing tools in their config > and git will be able to map the signature to the tool when verifying (e.g. > git log --show-signature). One problem with that for the signatures in tag bodies is that "signing-tool: gpg" looks like something a human might right (as opposed to the PEM boundary, which is a bit more obvious). If we're going to make up a micro-format, it may be simpler to just have something PEM-like in the first place, and shove signify into that. > > So _if_ we knew what it would take to support signify, we could > > potentially adjust what's going into 2.19 in order to skip straight to > > the more generic interface. But on the OTOH, it may not be worth > > rushing, and there is already a vague plan of how the gpg..* > > config would interact with a more generic config. > > there's no rush, but i would prefer that the newer patch set get changed to > use the more generic 'signingtool..*' instead of 'gpg..*'. if > you all agree, i'll follow up with a patch to change that part of what is > going into 2.19. I'm on the fence for that myself. The best way to get people to comment would be to make a patch, and cc people involved in the earlier discussions. -Peff
Re: [PATCH 1/1] verify-tag/verify-commit should exit unsuccessfully when signature is not trusted
On Wed, Aug 08, 2018 at 07:04:56PM -0400, Jeff King wrote: > On Sat, Aug 04, 2018 at 10:43:46AM +0200, Karel Kočí wrote: > > I have a solution for my problem (calling git verify-* twice and grep). > > That is > > not the point of this email nor this contribution. The point is that > > although > > GPG's behavior of exiting with 0 code when trust level is unknown is > > unexpected > > but in the end understandable, git's behavior of exiting with 0 code even > > if key > > is explicitly untrusted is just counterintuitive. I think that few people > > are > > still going to get nasty surprise when I consider that this change was > > introduced > > mid 2014 just after v2.4.0 and Ubuntu 14.04 lts (running even on part of our > > infrastructure) still contains version 1.9.1 and in that release it was > > acknowledging GPG exit code. > > FWIW, I'm on board with returning non-zero in any case where gpg would. I think that's probably the best solution overall. There's a bug report in Debian (https://bugs.debian.org/895048) that requests that behavior instead of the status quo, and also it's the behavior that's documented: gpg.program Use this custom program instead of "gpg" found on $PATH when making or verifying a PGP signature. The program must support the same command-line interface as GPG, namely, to verify a detached signature, "gpg --verify $file - <$signature" is run, and the program is expected to signal a good signature by exiting with code 0, and to generate an ASCII-armored detached signature, the standard input of "gpg -bsau $key" is fed with the contents to be signed, and the program is expected to send the result to its standard output. -- brian m. carlson: Houston, Texas, US OpenPGP: https://keybase.io/bk2204 signature.asc Description: PGP signature
[RFC PATCH] packfile: iterate packed objects in pack order
Many invocations of for_each_object_in_pack() and for_each_packed_object() (which invokes the former) subsequently check at least the type of the packed object, necessitating accessing the packfile itself. For locality reasons, it is thus better to iterate in pack order, instead of index order. Teach for_each_object_in_pack() to iterate in pack order by first creating a reverse index. This is based off work by Jeff King. Signed-off-by: Jonathan Tan --- After writing this patch and looking at it further, I'm not sure if this is a clear benefit, but here's the patch anyway. In particular, builtin/fsck.c and builtin/cat-file.c just deal with the OID directly and does not access the packfile at all (at least at the time of invoking for_each_packed_object). And revision.c, if we are excluding promisor objects, parses each packed promisor object, but it seems possible to avoid doing that (replacing the parse_object() by lookup_unknown_object() still passes tests). --- packfile.c | 10 +++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packfile.c b/packfile.c index 6974903e5..371b64e9b 100644 --- a/packfile.c +++ b/packfile.c @@ -15,6 +15,7 @@ #include "tree-walk.h" #include "tree.h" #include "object-store.h" +#include "pack-revindex.h" char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, @@ -1890,14 +1891,17 @@ int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void uint32_t i; int r = 0; + load_pack_revindex(p); + for (i = 0; i < p->num_objects; i++) { + uint32_t pack_nr = p->revindex[i].nr; struct object_id oid; - if (!nth_packed_object_oid(, p, i)) + if (!nth_packed_object_oid(, p, pack_nr)) return error("unable to get sha1 of object %u in %s", -i, p->pack_name); +pack_nr, p->pack_name); - r = cb(, p, i, data); + r = cb(, p, pack_nr, data); if (r) break; } -- 2.18.0.597.ga71716f1ad-goog
Re: [PATCH 1/1] verify-tag/verify-commit should exit unsuccessfully when signature is not trusted
On Sat, Aug 04, 2018 at 10:43:46AM +0200, Karel Kočí wrote: > > I think the only sensible thing is to err on the conservative side, and > > return non-zero if we saw _any_ invalid signature. > > > > I will note, though, that just checking the exit code of `verify-tag` > > isn't really that thorough. It shows that there was _a_ signature, but > > we don't know: > > > > - if it was an identity the user would expect to be signing tags > > > > - if it even matches the refname we used to find the tag > > > > So I'd argue that any real verification needs to either have a human in > > the loop, or implement a custom policy based on reading the full output. > > > > I know we (and you specifically Santiago) talked about this a while ago, > > and we ended up providing ways to get more information out of > > verify-tag, so that a tool could sit on top of that and implement more > > project-specific policy. I don't know offhand of any reusable tools that > > do so, though. > > I think that it would be even legit to exit on first tag verification > failure. If > someone wants to really verify all tags then it can be done with simple for > loop. > git that way does not have to solve problem of error combination. Yeah, I'd be fine with that. > > - if it was an identity the user would expect to be signing tags > That can be done just by using trust levels. I may be showing my PGP cluelessness here, but I thought trust levels were about saying "to what degree do I think this uid matches this key". Or are you proposing feeding gpg a fixed trust-db pre-seeded with keys that are allowed to sign? I suppose that would work, but: - is it much easier than just verifying the uid from gpg output against a trusted list? - it mixes authentication and authorization. I.e., you lose the ability to know about a case of "yes, this signature is valid by this person, but they are not an authorized tagger". Definitely for some use cases that is fine (and easier still is to just not even have disallowed keys in your keyring). But I don't think it's a general solution. > > - if it even matches the refname we used to find the tag > Can you explain this more? You mean that string (such as v1.1) used to lookup > tag > object is not verified as part of that object? Yes. The signature is just over the tag object contents itself. That object does contain a "tag" field, but it may or may not be the tag you're expecting. Git _could_ confirm that when you looked up a tag via refs/tags/v1.1 that the tag object we found has "tag v1.1" in it. But that's not always possible (you might feed Git a resolved name, for example). > OK I thing that it was enough of abstract concepts from me. Let me explain you > what am I trying to achieve. I am implementing feeds (in other words git > repositories with packages) and package sources verification for OpenWRT. We > (project Turris by CZ.NIC) are signing all our commits and all our tags. Now > we > are using small script that is verifying our repositories just before we run > build. That is against keyring maintained on our server. I am trying to extend > that to whole OpenWRT tree. That introduces problem of having a lot of keys > and a > lot of packages sharing same allowed keys. Fetching all allowed keys for every > package from key servers is just slow because of that I have to share those > between packages. In general there are two options. First one is to have > cache of > already fetched keys in armor format. Second one is to have one keyring and by > setting all keys explicitly as never trusted with package given exception. > Unfortunately first option can't be used because of one other request that is > from > our team. We don't want to be forced to update list of allowed contributors > to our > projects every time we have new colleague. Solution we come up with is to have > central PGP key that signs our whole team and then verification is done by > allowing GPG to fetch additional keys with max-cert-depth 1. That brings me > to git > verify-commit/tag that won't exit with zero code when signature is not > trusted. OK, that makes some sense (and I guess answers my "how would you use the trustdb" question above). > I have a solution for my problem (calling git verify-* twice and grep). That > is > not the point of this email nor this contribution. The point is that although > GPG's behavior of exiting with 0 code when trust level is unknown is > unexpected > but in the end understandable, git's behavior of exiting with 0 code even if > key > is explicitly untrusted is just counterintuitive. I think that few people are > still going to get nasty surprise when I consider that this change was > introduced > mid 2014 just after v2.4.0 and Ubuntu 14.04 lts (running even on part of our > infrastructure) still contains version 1.9.1 and in that release it was > acknowledging GPG exit code. FWIW, I'm on board with returning non-zero in any case where gpg
Re: [PATCH 0/5] chainlint: improve robustness against "unusual" shell coding
On Tue, Aug 07, 2018 at 04:21:30AM -0400, Eric Sunshine wrote: > This series improves chainlint's robustness when faced with the sort of > unusual shell coding in contrib/subtree/t7900 which triggered a > false-positive, as reported by Jonathan[1]. Jonathan has already > rewritten[2] that code to be cleaner and more easily understood (and, > consequently, to avoid triggering the false-positive), thus the > improvements in this series are not strictly necessary. > > Nevertheless, it seems prudent to make chainlint more robust against > such unusual coding as an aid to future less-experienced test writers, > making it less likely for them to trigger a false-positive and waste > time trying to decipher a non-existent problem (in their code). > > In [3], I said that 'sed' couldn't "be coerced" into dealing with nested > here-docs with arbitrary tag names (explaining why it recognized only a > "blessed" set of hard-coded names). However, I put a bit of thought into > it and figured out how to do it. Patch 1/5 is the result. > > This applies atop 'master'. I had two minor comments on the first patch. I'll admit my eyes glazed over looking at the rest of them, and to make any kind of intelligent review I'd need to spend an hour understanding how the sed script works. Which frankly, I'm not sure is worth it. Given the empirical results (both on the real code base and the new tests you add) and the low-risk nature (it's linting our tests, after all, not code users run), I'd be inclined to say it's not making anything worse, and probably making things better. We can find out about any further short-comings in the wild. -Peff
Re: [PATCH 1/5] chainlint: match arbitrary here-docs tags rather than hard-coded names
On Tue, Aug 07, 2018 at 04:21:31AM -0400, Eric Sunshine wrote: > diff --git a/t/chainlint.sed b/t/chainlint.sed > index 5f0882cb38..bd76c5d181 100644 > --- a/t/chainlint.sed > +++ b/t/chainlint.sed > @@ -61,6 +61,22 @@ > # "else", and "fi" in if-then-else likewise must not end with "&&", thus > # receives similar treatment. > # > +# Swallowing here-docs with arbitrary tags requires a bit of finesse. When a > +# line such as "catfront > +# of the line enclosed in angle brackets as a sentinel, giving "cat > >out". > +# As each subsequent line is read, it is appended to the target line and a > +# (whitespace-loose) back-reference match /^<(.*)>\n\1$/ is attempted to see > if > +# the content inside "<...>" matches the entirety of the newly-read line. For > +# instance, if the next line read is "some data", when concatenated with the > +# target line, it becomes "cat >out\nsome data", and a match is > attempted > +# to see if "EOF" matches "some data". Since it doesn't, the next line is > +# attempted. When a line consisting of only "EOF" (and possible whitespace) > is > +# encountered, it is appended to the target line giving "cat >out\nEOF", > +# in which case the "EOF" inside "<...>" does match the text following the > +# newline, thus the closing here-doc tag has been found. The closing tag line > +# and the "<...>" prefix on the target line are then discarded, leaving just > +# the target line "cat >out". Gross, but OK, as long as we would not get confused by a line that actually started with at the start. > +/<<[ ]*[-\\]*[A-Z0-9_][A-Z0-9_]*/ { > + s/^\(.*\)<<[]*[-\\]*\([A-Z0-9_][A-Z0-9_]*\)/<\2>\1< + s/[ ]*<
Re: [PATCH] update-index: there no longer is `apply --index-info`
On Wed, Aug 08, 2018 at 02:35:18PM -0700, Junio C Hamano wrote: > Back when we removed `git apply --index-info` in 2007, we forgot to > adjust the documentation for update-index that reads its output. > > Let's reorder the description of three formats to present the other > two formats that are still generated by git commands before this > format, and stop mentioning `git apply --index-info`. Yep, this makes sense. > --- > Documentation/git-update-index.txt | 15 ++- > 1 file changed, 6 insertions(+), 9 deletions(-) For fun, I tried out my "doc-diff" on it, and it looks good. :) -Peff
Re: What's cooking in git.git (Aug 2018, #02; Mon, 6)
Junio C Hamano writes: > Hmph, it came from this message (most headers omitted) > > To: =?iso-8859-1?Q?=C6var_Arnfj=F6r=F0?= Bjarmason > Message-ID: <20180804085247.ge55...@aiede.svl.corp.google.com> > Content-Type: text/plain; charset=iso-8859-1 > Content-Disposition: inline > Content-Transfer-Encoding: 8bit > > Subject: doc hash-function-transition: pick SHA-256 as NewHash > > ... > > Signed-off-by: Ævar Arnfjörð Bjarmason > > and the body seems to be correct iso-8859-1. "od -cx" tells me that > the file stores 0xf0 for that D looking thing, for example. Could it > be mailinfo that screws up, I wonder. A quick check with "git mailinfo" > does not give me anything suspicous---the info contents emitted to > its standard output is correctly converted to UTF-8. Puzzled... > > I read from public-inbox/git over nntp, if that matters. Just to close the loop, this turned out to be caused by my use of Gnus/Emacs. You can stop reading if you are not interested in reading and applying patches from inside Gnus. I am used to type '|' (gnus-summary-pipe-output) to pipe the current article into "git am -s[c3]", and it works fine when the payload is UTF-8. But the command decodes, and strips certain e-mail headers, before feeding it to the command. While the payload is converted to UTF-8 (presumably because that is what I use by default), "Content-type" is *not* among the e-mail headers that are stripped, so "am" ends up seeing UTF-8 bytestream that (still) claims to be "iso-8859-1" when processing the above message. I need to get used to typing M-i r | (that is, to use the 'r' "symbolic prefix") to force the message piped as-is to the command. Again, thanks for noticing and giving me a chance to correct the result before it got too late.
[PATCH v2 1/2] repack: refactor setup of pack-objects cmd
A subsequent patch will teach repack to run pack-objects with some same and some different arguments if repacking of promisor objects is required. Refactor the setup of the pack-objects cmd so that setting up the arguments common to both is done in a function. Signed-off-by: Jonathan Tan --- builtin/repack.c | 99 +++- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/builtin/repack.c b/builtin/repack.c index 6c636e159..f8cae7d66 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -138,6 +138,47 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name) strbuf_release(); } +struct pack_objects_args { + const char *window; + const char *window_memory; + const char *depth; + const char *threads; + const char *max_pack_size; + int no_reuse_delta; + int no_reuse_object; + int quiet; + int local; +}; + +static void prepare_pack_objects(struct child_process *cmd, +const struct pack_objects_args *args) +{ + argv_array_push(>args, "pack-objects"); + if (args->window) + argv_array_pushf(>args, "--window=%s", args->window); + if (args->window_memory) + argv_array_pushf(>args, "--window-memory=%s", args->window_memory); + if (args->depth) + argv_array_pushf(>args, "--depth=%s", args->depth); + if (args->threads) + argv_array_pushf(>args, "--threads=%s", args->threads); + if (args->max_pack_size) + argv_array_pushf(>args, "--max-pack-size=%s", args->max_pack_size); + if (args->no_reuse_delta) + argv_array_pushf(>args, "--no-reuse-delta"); + if (args->no_reuse_object) + argv_array_pushf(>args, "--no-reuse-object"); + if (args->local) + argv_array_push(>args, "--local"); + if (args->quiet) + argv_array_push(>args, "--quiet"); + if (delta_base_offset) + argv_array_push(>args, "--delta-base-offset"); + argv_array_push(>args, packtmp); + cmd->git_cmd = 1; + cmd->out = -1; +} + #define ALL_INTO_ONE 1 #define LOOSEN_UNREACHABLE 2 @@ -165,15 +206,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix) int delete_redundant = 0; const char *unpack_unreachable = NULL; int keep_unreachable = 0; - const char *window = NULL, *window_memory = NULL; - const char *depth = NULL; - const char *threads = NULL; - const char *max_pack_size = NULL; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; - int no_reuse_delta = 0, no_reuse_object = 0; int no_update_server_info = 0; - int quiet = 0; - int local = 0; + struct pack_objects_args po_args = {NULL}; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, _everything, @@ -183,14 +218,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix) LOOSEN_UNREACHABLE | ALL_INTO_ONE), OPT_BOOL('d', NULL, _redundant, N_("remove redundant packs, and run git-prune-packed")), - OPT_BOOL('f', NULL, _reuse_delta, + OPT_BOOL('f', NULL, _args.no_reuse_delta, N_("pass --no-reuse-delta to git-pack-objects")), - OPT_BOOL('F', NULL, _reuse_object, + OPT_BOOL('F', NULL, _args.no_reuse_object, N_("pass --no-reuse-object to git-pack-objects")), OPT_BOOL('n', NULL, _update_server_info, N_("do not run git-update-server-info")), - OPT__QUIET(, N_("be quiet")), - OPT_BOOL('l', "local", , + OPT__QUIET(_args.quiet, N_("be quiet")), + OPT_BOOL('l', "local", _args.local, N_("pass --local to git-pack-objects")), OPT_BOOL('b', "write-bitmap-index", _bitmaps, N_("write bitmap index")), @@ -198,15 +233,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("with -A, do not loosen objects older than this")), OPT_BOOL('k', "keep-unreachable", _unreachable, N_("with -a, repack unreachable objects")), - OPT_STRING(0, "window", , N_("n"), + OPT_STRING(0, "window", _args.window, N_("n"), N_("size of the window used for delta compression")), - OPT_STRING(0, "window-memory", _memory, N_("bytes"), + OPT_STRING(0, "window-memory", _args.window_memory, N_("bytes"), N_("same as the above, but limit memory size instead of entries count")), - OPT_STRING(0,
[PATCH v2 0/2] Repacking of promisor packfiles
Changes from v1: - add NEEDSWORK stating that input to pack-objects could be removed - run pack-objects to repack promisor objects only if there is at least one of them - this exposed a possible bug where the later part of cmd_repack() requires a correct packed_git (this was mostly noticed by tests that deal with corrupt packfiles), so insert a call to reprepare_packed_git() after all packfile manipulation is done - rename prepare_packed_objects() to prepare_pack_objects() to better reflect that we are preparing a call to pack-objects, not preparing existing packed objects Peff raised the possibility of making for_each_packed_object() use pack order instead of index order - I'll also take a look at that, separately from this patch series. Jonathan Tan (2): repack: refactor setup of pack-objects cmd repack: repack promisor objects if -a or -A is set Documentation/git-repack.txt | 5 + builtin/repack.c | 182 ++- t/t0410-partial-clone.sh | 85 +--- 3 files changed, 213 insertions(+), 59 deletions(-) -- 2.18.0.597.ga71716f1ad-goog
[PATCH v2 2/2] repack: repack promisor objects if -a or -A is set
Currently, repack does not touch promisor packfiles at all, potentially causing the performance of repositories that have many such packfiles to drop. Therefore, repack all promisor objects if invoked with -a or -A. This is done by an additional invocation of pack-objects on all promisor objects individually given, which takes care of deduplication and allows the resulting packfiles to respect flags such as --max-pack-size. Signed-off-by: Jonathan Tan --- Documentation/git-repack.txt | 5 +++ builtin/repack.c | 83 +-- t/t0410-partial-clone.sh | 85 +++- 3 files changed, 158 insertions(+), 15 deletions(-) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index d90e7907f..d05625096 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -40,6 +40,11 @@ OPTIONS Note that users fetching over dumb protocols will have to fetch the whole new pack in order to get any contained object, no matter how many other objects in that pack they already have locally. ++ +Promisor packfiles are repacked separately: if there are packfiles that +have an associated ".promisor" file, these packfiles will be repacked +into another separate pack, and an empty ".promisor" file corresponding +to the new separate pack will be written. -A:: Same as `-a`, unless `-d` is used. Then any unreachable diff --git a/builtin/repack.c b/builtin/repack.c index f8cae7d66..5c97dec3d 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -8,6 +8,7 @@ #include "strbuf.h" #include "string-list.h" #include "argv-array.h" +#include "packfile.h" static int delta_base_offset = 1; static int pack_kept_objects = -1; @@ -83,7 +84,7 @@ static void remove_pack_on_signal(int signo) /* * Adds all packs hex strings to the fname list, which do not - * have a corresponding .keep or .promisor file. These packs are not to + * have a corresponding .keep file. These packs are not to * be kept if we are going to pack everything into one file. */ static void get_non_kept_pack_filenames(struct string_list *fname_list, @@ -111,8 +112,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list, fname = xmemdupz(e->d_name, len); - if (!file_exists(mkpath("%s/%s.keep", packdir, fname)) && - !file_exists(mkpath("%s/%s.promisor", packdir, fname))) + if (!file_exists(mkpath("%s/%s.keep", packdir, fname))) string_list_append_nodup(fname_list, fname); else free(fname); @@ -122,7 +122,7 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list, static void remove_redundant_pack(const char *dir_name, const char *base_name) { - const char *exts[] = {".pack", ".idx", ".keep", ".bitmap"}; + const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"}; int i; struct strbuf buf = STRBUF_INIT; size_t plen; @@ -179,6 +179,76 @@ static void prepare_pack_objects(struct child_process *cmd, cmd->out = -1; } +/* + * Write oid to the given struct child_process's stdin, starting it first if + * necessary. + */ +static int write_oid(const struct object_id *oid, struct packed_git *pack, +uint32_t pos, void *data) +{ + struct child_process *cmd = data; + + if (cmd->in == -1) { + if (start_command(cmd)) + die("Could not start pack-objects to repack promisor objects"); + } + + xwrite(cmd->in, oid_to_hex(oid), GIT_SHA1_HEXSZ); + xwrite(cmd->in, "\n", 1); + return 0; +} + +static void repack_promisor_objects(const struct pack_objects_args *args, + struct string_list *names) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + FILE *out; + struct strbuf line = STRBUF_INIT; + + prepare_pack_objects(, args); + cmd.in = -1; + + /* +* NEEDSWORK: Giving pack-objects only the OIDs without any ordering +* hints may result in suboptimal deltas in the resulting pack. See if +* the OIDs can be sent with fake paths such that pack-objects can use a +* {type -> existing pack order} ordering when computing deltas instead +* of a {type -> size} ordering, which may produce better deltas. +*/ + for_each_packed_object(write_oid, , + FOR_EACH_OBJECT_PROMISOR_ONLY); + + if (cmd.in == -1) + /* No packed objects; cmd was never started */ + return; + + close(cmd.in); + + out = xfdopen(cmd.out, "r"); + while (strbuf_getline_lf(, out) != EOF) { + char *promisor_name; + int fd; + if (line.len != 40) + die("repack: Expecting 40 character sha1 lines only from pack-objects."); +
[PATCH 2/2] submodule: munge paths to submodule git directories
Commit 0383bbb901 (submodule-config: verify submodule names as paths, 2018-04-30) introduced some checks to ensure that submodule names don't include directory traversal components (e.g. "../"). This addresses the vulnerability identified in 0383bbb901 but the root cause is that we use submodule names to construct paths to the submodule's git directory. What we really should do is munge the submodule name before using it to construct a path. Teach "submodule_name_to_gitdir()" to munge a submodule's name (by url encoding it) before using it to build a path to the submodule's gitdir. Signed-off-by: Brandon Williams --- submodule.c | 14 ++ t/t7400-submodule-basic.sh | 32 +++- t/t7406-submodule-update.sh | 21 ++--- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/submodule.c b/submodule.c index 24eced34e7..4854d88ce8 100644 --- a/submodule.c +++ b/submodule.c @@ -1965,6 +1965,20 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule) void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r, const char *submodule_name) { + size_t modules_len; + strbuf_git_common_path(buf, r, "modules/"); + modules_len = buf->len; strbuf_addstr(buf, submodule_name); + + /* +* If the submodule gitdir already exists using the old-fashioned +* location (which uses the submodule name as-is, without munging it) +* then return that. +*/ + if (!access(buf->buf, F_OK)) + return; + + strbuf_setlen(buf, modules_len); + strbuf_addstr_urlencode(buf, submodule_name, 1); } diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 2c2c97e144..963693332c 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -933,7 +933,7 @@ test_expect_success 'recursive relative submodules stay relative' ' cd clone2 && git submodule update --init --recursive && echo "gitdir: ../.git/modules/sub3" >./sub3/.git_expect && - echo "gitdir: ../../../.git/modules/sub3/modules/dirdir/subsub" >./sub3/dirdir/subsub/.git_expect + echo "gitdir: ../../../.git/modules/sub3/modules/dirdir%2fsubsub" >./sub3/dirdir/subsub/.git_expect ) && test_cmp clone2/sub3/.git_expect clone2/sub3/.git && test_cmp clone2/sub3/dirdir/subsub/.git_expect clone2/sub3/dirdir/subsub/.git @@ -1324,4 +1324,34 @@ test_expect_success 'recursive clone respects -q' ' test_must_be_empty actual ' +test_expect_success 'resolve submodule gitdir in superprojects modules directory' ' + test_when_finished "rm -rf superproject submodule" && + + # Create a superproject with a submodule which contains a "/" + test_create_repo submodule && + test_commit -C submodule one && + test_create_repo superproject && + git -C superproject submodule add ../submodule sub/module && + git -C superproject commit -m "add submodule" && + + # "/" characters in submodule names are properly urlencoded before + # being used to construct a path to the submodules gitdir. + cat >expect <<-EOF && + $(git -C superproject rev-parse --git-common-dir)/modules/sub%2fmodule + EOF + git -C superproject submodule--helper gitdir "sub/module" >actual && + test_cmp expect actual && + test_path_is_dir "superproject/.git/modules/sub%2fmodule" && + + # Test the old-fashioned way of storing submodules in the + # "modules" directory by directly renaming the submodules gitdir + mkdir superproject/.git/modules/sub/ && + mv superproject/.git/modules/sub%2fmodule superproject/.git/modules/sub/module && + cat >expect <<-EOF && + $(git -C superproject rev-parse --git-common-dir)/modules/sub/module + EOF + git -C superproject submodule--helper gitdir "sub/module" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index f604ef7a72..fb744c5c39 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -777,12 +777,8 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir' ' (cd super && mkdir deeper && git submodule add ../submodule deeper/submodule && -(cd deeper/submodule && - git log > ../../expected -) && -(cd .git/modules/deeper/submodule && - git log > ../../../../actual -) && +git -C deeper/submodule log >expected && +git -C .git/modules/deeper%2fsubmodule log >actual && test_cmp actual expected ) ' @@ -795,12 +791,9 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir' ' (cd super2 && git submodule init deeper/submodule && git
[PATCH 1/2] submodule: create helper to build paths to submodule gitdirs
Introduce a helper function "submodule_name_to_gitdir()" (and the submodule--helper subcommand "gitdir") which constructs a path to a submodule's gitdir, located in the provided repository's "modules" directory. This consolidates the logic needed to build up a path into a repository's "modules" directory, abstracting away the fact that submodule git directories are stored in a repository's common gitdir. This makes it easier to adjust how submodules gitdir are stored in the "modules" directory in a future patch. Signed-off-by: Brandon Williams --- builtin/submodule--helper.c | 28 +++-- dir.c| 2 +- git-submodule.sh | 7 +++-- repository.c | 3 +- submodule.c | 53 submodule.h | 7 + t/t7410-submodule-checkout-to.sh | 6 ++-- 7 files changed, 75 insertions(+), 31 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index a3c4564c6c..5bfd2d0be9 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -906,6 +906,21 @@ static int module_name(int argc, const char **argv, const char *prefix) return 0; } +static int module_gitdir(int argc, const char **argv, const char *prefix) +{ + struct strbuf gitdir = STRBUF_INIT; + + if (argc != 2) + usage(_("git submodule--helper gitdir ")); + + submodule_name_to_gitdir(, the_repository, argv[1]); + + printf("%s\n", gitdir.buf); + + strbuf_release(); + return 0; +} + struct sync_cb { const char *prefix; unsigned int flags; @@ -1268,18 +1283,24 @@ static int add_possible_reference_from_superproject( * standard layout with .git/(modules/)+/objects */ if (ends_with(alt->path, "/objects")) { + struct repository alternate; char *sm_alternate; struct strbuf sb = STRBUF_INIT; struct strbuf err = STRBUF_INIT; strbuf_add(, alt->path, strlen(alt->path) - strlen("objects")); + repo_init(, sb.buf, NULL); + /* * We need to end the new path with '/' to mark it as a dir, * otherwise a submodule name containing '/' will be broken * as the last part of a missing submodule reference would * be taken as a file name. */ - strbuf_addf(, "modules/%s/", sas->submodule_name); + strbuf_reset(); + submodule_name_to_gitdir(, , sas->submodule_name); + strbuf_addch(, '/'); + repo_clear(); sm_alternate = compute_alternate_path(sb.buf, ); if (sm_alternate) { @@ -1392,7 +1413,7 @@ static int module_clone(int argc, const char **argv, const char *prefix) usage_with_options(git_submodule_helper_usage, module_clone_options); - strbuf_addf(, "%s/modules/%s", get_git_dir(), name); + submodule_name_to_gitdir(, the_repository, name); sm_gitdir = absolute_pathdup(sb.buf); strbuf_reset(); @@ -2018,7 +2039,7 @@ static int connect_gitdir_workingtree(int argc, const char **argv, const char *p name = argv[1]; path = argv[2]; - strbuf_addf(, "%s/modules/%s", get_git_dir(), name); + submodule_name_to_gitdir(, the_repository, name); sm_gitdir = absolute_pathdup(sb.buf); connect_work_tree_and_git_dir(path, sm_gitdir, 0); @@ -2040,6 +2061,7 @@ struct cmd_struct { static struct cmd_struct commands[] = { {"list", module_list, 0}, {"name", module_name, 0}, + {"gitdir", module_gitdir, 0}, {"clone", module_clone, 0}, {"update-clone", update_clone, 0}, {"connect-gitdir-workingtree", connect_gitdir_workingtree, 0}, diff --git a/dir.c b/dir.c index 21e6f2520a..7a9827ea4b 100644 --- a/dir.c +++ b/dir.c @@ -3053,7 +3053,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree, strbuf_reset(_wt); strbuf_reset(_gd); strbuf_addf(_wt, "%s/%s", sub_worktree, sub->path); - strbuf_addf(_gd, "%s/modules/%s", sub_gitdir, sub->name); + submodule_name_to_gitdir(_gd, , sub->name); connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1); } diff --git a/git-submodule.sh b/git-submodule.sh index 8b5ad59bde..053747d290 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -252,12 +252,13 @@ Use -f if you really want to add it." >&2 fi else - if test -d ".git/modules/$sm_name" + sm_gitdir="$(git submodule--helper gitdir "$sm_name")" + if test -d "$sm_gitdir" then if test -z "$force" then
[PATCH 0/2] munge submodule names
Here's a more polished series taking into account some of the feedback on the RFC. As Junio pointed out URL encoding makes the directories much more human readable, but I'm open to other ideas if we don't think URL encoding is the right thing to do. Brandon Williams (2): submodule: create helper to build paths to submodule gitdirs submodule: munge paths to submodule git directories builtin/submodule--helper.c | 28 +++-- dir.c| 2 +- git-submodule.sh | 7 ++-- repository.c | 3 +- submodule.c | 67 ++-- submodule.h | 7 t/t7400-submodule-basic.sh | 32 ++- t/t7406-submodule-update.sh | 21 +++--- t/t7410-submodule-checkout-to.sh | 6 ++- 9 files changed, 126 insertions(+), 47 deletions(-) -- 2.18.0.597.ga71716f1ad-goog
Re: [PATCH v2] clone: report duplicate entries on case-insensitive filesystems
On Wed, Aug 08, 2018 at 03:48:04PM -0400, Jeff Hostetler wrote: > > ce_match_stat() may not be a very good measure to see if two paths > > refer to the same file, though. After a fresh checkout, I would not > > be surprised if two completely unrelated paths have the same size > > and have same mtime/ctime. In its original use case, i.e. "I have > > one specific path in mind and took a snapshot of its metadata > > earlier. Is it still the same, or has it been touched?", that may > > be sufficient to detect that the path has been _modified_, but > > without reliable inum, it may be a poor measure to say two paths > > refer to the same. > > I agree with Junio on this one. The mtime values are sloppy at best. > On FAT file systems, they have 2 second resolution. Even NTFS IIRC > has only 100ns resolution, so we might get a lot of false matches > using this technique, right? Yeah, I think anything less than inode (or some system equivalent) is going to be too flaky. > It might be better to build an equivalence-class hash-map for the > colliding entries. Compute a "normalized" version of the pathname > (such as convert to lowercase, strip final-dots/spaces, strip the > digits following tilda of a shortname, and etc for the MAC's UTF-isms). > Then when you rescan the index entries to find the matches, apply the > equivalence operator on the pathname and do the hashmap lookup. > When you find a match, you have a "potential" collider pair (I say > potential only because of the ambiguity of shortnames). Then we > can use inum/file-index/whatever to see if they actually collide. I think we really want to avoid doing that normalization ourselves if we can. There are just too many filesystem-specific rules. If we have an equivalence-class hashmap and feed it inodes (or again, some system equivalent) as the keys, we should get buckets of collisions. I started to write a "something like this..." earlier, but got bogged down in boilerplate around the C hashmap. But here it is in perl. ;) -- >8 -- # pretend we have these paths in our index paths='foo FOO and some other paths' # create them; this will make a single path on a case-insensitive system for i in $paths; do echo $i >$i done # now find the duplicates perl -le ' for my $path (@ARGV) { # this would be an ntfs unique-id on Windows my $inode = (lstat($path))[1]; push @{$h{$inode}}, $path; } for my $group (grep { @$_ > 1 } values(%h)) { print "group:"; print " ", $_ for (@$group); } ' $paths -- >8 -- which should show the obvious pair (it does for me on vfat-on-linux, though where it gets those inodes from, I have no idea ;) ). -Peff
Re: [PATCH 1/1] pull --rebase=: allow single-letter abbreviations for the type
Junio C Hamano writes: > Ævar Arnfjörð Bjarmason writes: > >>> - else if (!strcmp(value, "preserve")) >>> + else if (!strcmp(value, "preserve") || !strcmp(value, "p")) >>> return REBASE_PRESERVE; >>> - else if (!strcmp(value, "merges")) >>> + else if (!strcmp(value, "merges") || !strcmp(value, "m")) >>> return REBASE_MERGES; >>> - else if (!strcmp(value, "interactive")) >>> + else if (!strcmp(value, "interactive") || !strcmp(value, "i")) >>> return REBASE_INTERACTIVE; >> >> Here 3 special cases are added... >> ... >>> +test_expect_success 'pull --rebase=i' ' >>> ... >>> +' >>> + >>> test_expect_success 'pull.rebase=invalid fails' ' >>> git reset --hard before-preserve-rebase && >>> test_config pull.rebase invalid && >> >> ...but this test is only for 1/3. I haven't run this, but it looks like >> the tests will still pass if we remove --rebase=p and --rebase=m. > > Good eyes. It's not like that parsing these three is implemented > with one thing; in other words, it is not hard to break one without > breaking the other two. Having said that, that can be done as a follow-up "oops, the original was sloppy" patch. It's not as bad compared to "oops, the original was totally borked and here is a fix", so I am OK with that ;-)
[PATCH 10/10] fetch: retry fetching submodules if sha1 were not fetched
Currently when git-fetch is asked to recurse into submodules, it dispatches a plain "git-fetch -C " (and some submodule related options such as prefix and recusing strategy, but) without any information of the remote or the tip that should be fetched. This works surprisingly well in some workflows (such as using submodules as a third party library), while not so well in other scenarios, such as in a Gerrit topic-based workflow, that can tie together changes (potentially across repositories) on the server side. One of the parts of such a Gerrit workflow is to download a change when wanting to examine it, and you'd want to have its submodule changes that are in the same topic downloaded as well. However these submodule changes reside in their own repository in their on ref (refs/changes/). Retry fetching a submodule if the object id that the superproject points to, cannot be found. Note: This is an RFC and doesn't support fetching to FETCH_HEAD yet, but only into a local branch. To make fetching into FETCH_HEAD work, we need some refactoring in builtin/fetch.c to adjust the calls to 'check_for_new_submodule_commits'. Signed-off-by: Stefan Beller --- builtin/fetch.c | 9 ++--- submodule.c | 79 - t/t5526-fetch-submodules.sh | 16 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 34d2bd123b3..9396e6a44c6 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -700,8 +700,7 @@ static int update_local_ref(struct ref *ref, what = _("[new ref]"); } - if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && - (recurse_submodules != RECURSE_SUBMODULES_ON)) + if (recurse_submodules != RECURSE_SUBMODULES_OFF) check_for_new_submodule_commits(>new_oid); r = s_update_ref(msg, ref, 0); format_display(display, r ? '!' : '*', what, @@ -716,8 +715,7 @@ static int update_local_ref(struct ref *ref, strbuf_add_unique_abbrev(, >object.oid, DEFAULT_ABBREV); strbuf_addstr(, ".."); strbuf_add_unique_abbrev(, >new_oid, DEFAULT_ABBREV); - if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && - (recurse_submodules != RECURSE_SUBMODULES_ON)) + if (recurse_submodules != RECURSE_SUBMODULES_OFF) check_for_new_submodule_commits(>new_oid); r = s_update_ref("fast-forward", ref, 1); format_display(display, r ? '!' : ' ', quickref.buf, @@ -731,8 +729,7 @@ static int update_local_ref(struct ref *ref, strbuf_add_unique_abbrev(, >object.oid, DEFAULT_ABBREV); strbuf_addstr(, "..."); strbuf_add_unique_abbrev(, >new_oid, DEFAULT_ABBREV); - if ((recurse_submodules != RECURSE_SUBMODULES_OFF) && - (recurse_submodules != RECURSE_SUBMODULES_ON)) + if (recurse_submodules != RECURSE_SUBMODULES_OFF) check_for_new_submodule_commits(>new_oid); r = s_update_ref("forced-update", ref, 1); format_display(display, r ? '!' : '+', quickref.buf, diff --git a/submodule.c b/submodule.c index ec7ea6f8c2d..6cbd0b1a470 100644 --- a/submodule.c +++ b/submodule.c @@ -1127,6 +1127,7 @@ struct submodule_parallel_fetch { int result; struct string_list changed_submodule_names; + struct string_list retry; }; #define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, STRING_LIST_INIT_DUP } @@ -1259,8 +1260,10 @@ static int get_next_submodule(struct child_process *cp, { int ret = 0; struct submodule_parallel_fetch *spf = data; + struct string_list_item *retry_it; for (; spf->count < spf->r->index->cache_nr; spf->count++) { + int recurse_config; struct strbuf submodule_prefix = STRBUF_INIT; const struct cache_entry *ce = spf->r->index->cache[spf->count]; const char *git_dir, *default_argv; @@ -1280,7 +1283,9 @@ static int get_next_submodule(struct child_process *cp, } } - switch (get_fetch_recurse_config(submodule, spf)) + recurse_config = get_fetch_recurse_config(submodule, spf); + + switch (recurse_config) { default: case RECURSE_SUBMODULES_DEFAULT: @@ -1318,9 +1323,46 @@ static int get_next_submodule(struct child_process *cp, strbuf_release(_prefix); if (ret) { spf->count++; + if (submodule != _submodule) + /* discard const-ness: */ + *task_cb = (void*)submodule; return 1; }
[PATCH 09/10] submodule: fetch in submodules git directory instead of in worktree
This patch started as a refactoring to make 'get_next_submodule' more readable, but upon doing so, I realized that git-fetch actually doesn't need to be run in the worktree. So let's run it in the git dir instead. That should pave the way towards fetching submodules that are currently not checked out. Signed-off-by: Stefan Beller --- submodule.c | 43 ++--- t/t5526-fetch-submodules.sh | 7 +- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/submodule.c b/submodule.c index 21757e32908..ec7ea6f8c2d 100644 --- a/submodule.c +++ b/submodule.c @@ -481,6 +481,12 @@ void prepare_submodule_repo_env(struct argv_array *out) DEFAULT_GIT_DIR_ENVIRONMENT); } +static void prepare_submodule_repo_env_in_gitdir(struct argv_array *out) +{ + prepare_submodule_repo_env_no_git_dir(out); + argv_array_pushf(out, "%s=.", GIT_DIR_ENVIRONMENT); +} + /* Helper function to display the submodule header line prior to the full * summary output. If it can locate the submodule objects directory it will * attempt to lookup both the left and right commits and put them into the @@ -1227,6 +1233,27 @@ static int get_fetch_recurse_config(const struct submodule *submodule, return spf->default_option; } +static const char *get_submodule_git_dir(struct repository *r, const char *path) +{ + struct repository subrepo; + const char *ret; + + if (repo_submodule_init(, r, path)) { + /* no entry in .gitmodules? */ + struct strbuf gitdir = STRBUF_INIT; + strbuf_repo_worktree_path(, r, "%s/.git", path); + if (repo_init(, gitdir.buf, NULL)) { + strbuf_release(); + return NULL; + } + } + + ret = xstrdup(subrepo.gitdir); + repo_clear(); + + return ret; +} + static int get_next_submodule(struct child_process *cp, struct strbuf *err, void *data, void **task_cb) { @@ -1234,8 +1261,6 @@ static int get_next_submodule(struct child_process *cp, struct submodule_parallel_fetch *spf = data; for (; spf->count < spf->r->index->cache_nr; spf->count++) { - struct strbuf submodule_path = STRBUF_INIT; - struct strbuf submodule_git_dir = STRBUF_INIT; struct strbuf submodule_prefix = STRBUF_INIT; const struct cache_entry *ce = spf->r->index->cache[spf->count]; const char *git_dir, *default_argv; @@ -1273,16 +1298,12 @@ static int get_next_submodule(struct child_process *cp, continue; } - strbuf_repo_worktree_path(_path, spf->r, "%s", ce->name); - strbuf_addf(_git_dir, "%s/.git", submodule_path.buf); strbuf_addf(_prefix, "%s%s/", spf->prefix, ce->name); - git_dir = read_gitfile(submodule_git_dir.buf); - if (!git_dir) - git_dir = submodule_git_dir.buf; - if (is_directory(git_dir)) { + git_dir = get_submodule_git_dir(spf->r, ce->name); + if (git_dir) { child_process_init(cp); - cp->dir = strbuf_detach(_path, NULL); - prepare_submodule_repo_env(>env_array); + prepare_submodule_repo_env_in_gitdir(>env_array); + cp->dir = git_dir; cp->git_cmd = 1; if (!spf->quiet) strbuf_addf(err, "Fetching submodule %s%s\n", @@ -1294,8 +1315,6 @@ static int get_next_submodule(struct child_process *cp, argv_array_push(>args, submodule_prefix.buf); ret = 1; } - strbuf_release(_path); - strbuf_release(_git_dir); strbuf_release(_prefix); if (ret) { spf->count++; diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 0f730d77815..4437cb17698 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -567,7 +567,12 @@ test_expect_success 'fetching submodule into a broken repository' ' test_must_fail git -C dst status && test_must_fail git -C dst diff && - test_must_fail git -C dst fetch --recurse-submodules + + # git-fetch cannot find the git directory of the submodule, + # so it will do nothing, successfully, as it cannot distinguish between + # this broken submodule and a submodule that was just set active but + # not cloned yet + git -C dst fetch --recurse-submodules ' test_expect_success "fetch new commits when submodule got renamed" ' -- 2.18.0.597.ga71716f1ad-goog
[PATCH 07/10] submodule: move global changed_submodule_names into fetch submodule struct
The `changed_submodule_names` are only used for fetching, so let's make it part of the struct that is passed around for fetching submodules. Signed-off-by: Stefan Beller --- submodule.c | 42 +++--- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/submodule.c b/submodule.c index 89a46b8af50..92988239f6b 100644 --- a/submodule.c +++ b/submodule.c @@ -24,7 +24,7 @@ #include "object-store.h" static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; -static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP; + static int initialized_fetch_ref_tips; static struct oid_array ref_tips_before_fetch; static struct oid_array ref_tips_after_fetch; @@ -1110,7 +1110,22 @@ void check_for_new_submodule_commits(struct object_id *oid) oid_array_append(_tips_after_fetch, oid); } -static void calculate_changed_submodule_paths(void) +struct submodule_parallel_fetch { + int count; + struct argv_array args; + struct repository *r; + const char *prefix; + int command_line_option; + int default_option; + int quiet; + int result; + + struct string_list changed_submodule_names; +}; +#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, STRING_LIST_INIT_DUP } + +static void calculate_changed_submodule_paths( + struct submodule_parallel_fetch *spf) { struct argv_array argv = ARGV_ARRAY_INIT; struct string_list changed_submodules = STRING_LIST_INIT_DUP; @@ -1148,7 +1163,8 @@ static void calculate_changed_submodule_paths(void) continue; if (!submodule_has_commits(path, commits)) - string_list_append(_submodule_names, name->string); + string_list_append(>changed_submodule_names, + name->string); } free_submodules_oids(_submodules); @@ -1185,18 +1201,6 @@ int submodule_touches_in_range(struct object_id *excl_oid, return ret; } -struct submodule_parallel_fetch { - int count; - struct argv_array args; - struct repository *r; - const char *prefix; - int command_line_option; - int default_option; - int quiet; - int result; -}; -#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0} - static int get_fetch_recurse_config(const struct submodule *submodule, struct submodule_parallel_fetch *spf) { @@ -1256,7 +1260,7 @@ static int get_next_submodule(struct child_process *cp, case RECURSE_SUBMODULES_DEFAULT: case RECURSE_SUBMODULES_ON_DEMAND: if (!submodule || !string_list_lookup( - _submodule_names, + >changed_submodule_names, submodule->name)) continue; default_argv = "on-demand"; @@ -1348,8 +1352,8 @@ int fetch_populated_submodules(struct repository *r, argv_array_push(, "--recurse-submodules-default"); /* default value, "--submodule-prefix" and its value are added later */ - calculate_changed_submodule_paths(); - string_list_sort(_submodule_names); + calculate_changed_submodule_paths(); + string_list_sort(_submodule_names); run_processes_parallel(max_parallel_jobs, get_next_submodule, fetch_start_failure, @@ -1358,7 +1362,7 @@ int fetch_populated_submodules(struct repository *r, argv_array_clear(); out: - string_list_clear(_submodule_names, 1); + string_list_clear(_submodule_names, 1); return spf.result; } -- 2.18.0.597.ga71716f1ad-goog
[PATCH 08/10] submodule.c: do not copy around submodule list
'calculate_changed_submodule_paths' uses a local list to compute the changed submodules, and then produces the result by copying appropriate items into the result list. Instead use the result list directly and prune items afterwards using string_list_remove_empty_items. As a side effect, we'll have access to the util pointer for longer that contains the commits that we need to fetch. Signed-off-by: Stefan Beller --- submodule.c | 19 ++- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/submodule.c b/submodule.c index 92988239f6b..21757e32908 100644 --- a/submodule.c +++ b/submodule.c @@ -1128,8 +1128,7 @@ static void calculate_changed_submodule_paths( struct submodule_parallel_fetch *spf) { struct argv_array argv = ARGV_ARRAY_INIT; - struct string_list changed_submodules = STRING_LIST_INIT_DUP; - const struct string_list_item *name; + struct string_list_item *name; /* No need to check if there are no submodules configured */ if (!submodule_from_path(the_repository, NULL, NULL)) @@ -1146,9 +1145,9 @@ static void calculate_changed_submodule_paths( * Collect all submodules (whether checked out or not) for which new * commits have been recorded upstream in "changed_submodule_names". */ - collect_changed_submodules(_submodules, ); + collect_changed_submodules(>changed_submodule_names, ); - for_each_string_list_item(name, _submodules) { + for_each_string_list_item(name, >changed_submodule_names) { struct oid_array *commits = name->util; const struct submodule *submodule; const char *path = NULL; @@ -1162,12 +1161,14 @@ static void calculate_changed_submodule_paths( if (!path) continue; - if (!submodule_has_commits(path, commits)) - string_list_append(>changed_submodule_names, - name->string); + if (submodule_has_commits(path, commits)) { + oid_array_clear(commits); + *name->string = '\0'; + } } - free_submodules_oids(_submodules); + string_list_remove_empty_items(>changed_submodule_names, 1); + argv_array_clear(); oid_array_clear(_tips_before_fetch); oid_array_clear(_tips_after_fetch); @@ -1362,7 +1363,7 @@ int fetch_populated_submodules(struct repository *r, argv_array_clear(); out: - string_list_clear(_submodule_names, 1); + free_submodules_oids(_submodule_names); return spf.result; } -- 2.18.0.597.ga71716f1ad-goog
[PATCH 05/10] submodule.c: fix indentation
The submodule subsystem is really bad at staying within 80 characters. Fix it while we are here. Signed-off-by: Stefan Beller --- submodule.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/submodule.c b/submodule.c index 5b4e5227d90..bceeba13217 100644 --- a/submodule.c +++ b/submodule.c @@ -1244,7 +1244,8 @@ static int get_next_submodule(struct child_process *cp, if (!submodule) { const char *name = default_name_or_path(ce->name); if (name) { - default_submodule.path = default_submodule.name = name; + default_submodule.path = name; + default_submodule.name = name; submodule = _submodule; } } @@ -1254,8 +1255,9 @@ static int get_next_submodule(struct child_process *cp, default: case RECURSE_SUBMODULES_DEFAULT: case RECURSE_SUBMODULES_ON_DEMAND: - if (!submodule || !unsorted_string_list_lookup(_submodule_names, -submodule->name)) + if (!submodule || !unsorted_string_list_lookup( + _submodule_names, + submodule->name)) continue; default_argv = "on-demand"; break; -- 2.18.0.597.ga71716f1ad-goog
[PATCH 04/10] submodule.c: convert submodule_move_head new argument to object id
All callers use oid_to_hex to convert the desired oid to a string before calling submodule_move_head. Defer the conversion to the submodule_move_head as it will turn out to be useful in a bit. Signed-off-by: Stefan Beller --- entry.c| 6 +++--- submodule.c| 12 ++-- submodule.h| 2 +- unpack-trees.c | 13 + 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/entry.c b/entry.c index b5d1d3cf231..4b34dfd30df 100644 --- a/entry.c +++ b/entry.c @@ -358,7 +358,7 @@ static int write_entry(struct cache_entry *ce, sub = submodule_from_ce(ce); if (sub) return submodule_move_head(ce->name, - NULL, oid_to_hex(>oid), + NULL, >oid, state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0); break; @@ -438,10 +438,10 @@ int checkout_entry(struct cache_entry *ce, unlink_or_warn(ce->name); return submodule_move_head(ce->name, - NULL, oid_to_hex(>oid), 0); + NULL, >oid, 0); } else return submodule_move_head(ce->name, - "HEAD", oid_to_hex(>oid), + "HEAD", >oid, state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0); } diff --git a/submodule.c b/submodule.c index 6e14547e9e0..5b4e5227d90 100644 --- a/submodule.c +++ b/submodule.c @@ -1597,9 +1597,9 @@ static void submodule_reset_index(const char *path) * pass NULL for old or new respectively. */ int submodule_move_head(const char *path, -const char *old_head, -const char *new_head, -unsigned flags) + const char *old_head, + const struct object_id *new_oid, + unsigned flags) { int ret = 0; struct child_process cp = CHILD_PROCESS_INIT; @@ -1679,7 +1679,7 @@ int submodule_move_head(const char *path, if (!(flags & SUBMODULE_MOVE_HEAD_FORCE)) argv_array_push(, old_head ? old_head : empty_tree_oid_hex()); - argv_array_push(, new_head ? new_head : empty_tree_oid_hex()); + argv_array_push(, new_oid ? oid_to_hex(new_oid) : empty_tree_oid_hex()); if (run_command()) { ret = error(_("Submodule '%s' could not be updated."), path); @@ -1687,7 +1687,7 @@ int submodule_move_head(const char *path, } if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) { - if (new_head) { + if (new_oid) { child_process_init(); /* also set the HEAD accordingly */ cp.git_cmd = 1; @@ -1696,7 +1696,7 @@ int submodule_move_head(const char *path, prepare_submodule_repo_env(_array); argv_array_pushl(, "update-ref", "HEAD", -"--no-deref", new_head, NULL); +"--no-deref", oid_to_hex(new_oid), NULL); if (run_command()) { ret = -1; diff --git a/submodule.h b/submodule.h index 4644683e6cb..d1cceabb9b7 100644 --- a/submodule.h +++ b/submodule.h @@ -118,7 +118,7 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule); #define SUBMODULE_MOVE_HEAD_FORCE (1<<1) extern int submodule_move_head(const char *path, const char *old, - const char *new_head, + const struct object_id *new_oid, unsigned flags); void submodule_unset_core_worktree(const struct submodule *sub); diff --git a/unpack-trees.c b/unpack-trees.c index f9efee0836a..6c76bd6162a 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -256,7 +256,7 @@ static void display_error_msgs(struct unpack_trees_options *o) static int check_submodule_move_head(const struct cache_entry *ce, const char *old_id, -const char *new_id, +const struct object_id *new_id, struct unpack_trees_options *o) { unsigned flags = SUBMODULE_MOVE_HEAD_DRY_RUN; @@ -1513,7 +1513,7 @@ static int verify_uptodate_1(const struct cache_entry *ce, if (submodule_from_ce(ce)) { int r = check_submodule_move_head(ce, - "HEAD", oid_to_hex(>oid), o); + "HEAD", >oid, o); if (r) return o->gently ? -1 :
[PATCH 03/10] sha1-array: provide oid_array_remove_if
Signed-off-by: Stefan Beller --- sha1-array.c | 39 +++ sha1-array.h | 3 +++ 2 files changed, 42 insertions(+) diff --git a/sha1-array.c b/sha1-array.c index 265941fbf40..10eb08b425e 100644 --- a/sha1-array.c +++ b/sha1-array.c @@ -77,3 +77,42 @@ int oid_array_for_each_unique(struct oid_array *array, } return 0; } + +int oid_array_remove_if(struct oid_array *array, + for_each_oid_fn fn, + void *data) +{ + int i, j; + char *to_remove = xcalloc(array->nr, sizeof(char)); + + /* No oid_array_sort() here! See the api-oid-array.txt docs! */ + + for (i = 0; i < array->nr; i++) { + int ret = fn(array->oid + i, data); + if (ret) + to_remove[i] = 1; + } + + i = 0, j = 0; + while (i < array->nr && j < array->nr) { + while (i < array->nr && !to_remove[i]) + i++; + /* i at first marked for deletion or out */ + if (j < i) + j = i; + while (j < array->nr && to_remove[j]) + j++; + /* j > i; j at first valid after first deletion range or out */ + if (i < array->nr && j < array->nr) + oidcpy(>oid[i], >oid[j]); + else if (i >= array->nr) + assert(j >= array->nr); + /* no pruning happened, keep original array->nr */ + else if (j >= array->nr) + array->nr = i; + } + + free(to_remove); + + return 0; +} diff --git a/sha1-array.h b/sha1-array.h index 232bf950172..151c7ad7f30 100644 --- a/sha1-array.h +++ b/sha1-array.h @@ -22,5 +22,8 @@ int oid_array_for_each(struct oid_array *array, int oid_array_for_each_unique(struct oid_array *array, for_each_oid_fn fn, void *data); +int oid_array_remove_if(struct oid_array *array, + for_each_oid_fn fn, + void *data); #endif /* SHA1_ARRAY_H */ -- 2.18.0.597.ga71716f1ad-goog
[PATCH 06/10] submodule.c: sort changed_submodule_names before searching it
Instead of sorting it after we created an unsorted list, we could insert correctly into the list. As the unsorted append is in order of cache entry names, this is already sorted if names were equal to paths for submodules. As submodule names are often the same as their path, the input is sorted pretty well already, so let's just do the sort afterwards. Signed-off-by: Stefan Beller --- submodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/submodule.c b/submodule.c index bceeba13217..89a46b8af50 100644 --- a/submodule.c +++ b/submodule.c @@ -1255,7 +1255,7 @@ static int get_next_submodule(struct child_process *cp, default: case RECURSE_SUBMODULES_DEFAULT: case RECURSE_SUBMODULES_ON_DEMAND: - if (!submodule || !unsorted_string_list_lookup( + if (!submodule || !string_list_lookup( _submodule_names, submodule->name)) continue; @@ -1349,6 +1349,7 @@ int fetch_populated_submodules(struct repository *r, /* default value, "--submodule-prefix" and its value are added later */ calculate_changed_submodule_paths(); + string_list_sort(_submodule_names); run_processes_parallel(max_parallel_jobs, get_next_submodule, fetch_start_failure, -- 2.18.0.597.ga71716f1ad-goog
[PATCH 01/10] string_list: print_string_list to use trace_printf
It is a debugging aid, so it should print to the debugging channel. Signed-off-by: Stefan Beller --- string-list.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/string-list.c b/string-list.c index 771c4550980..9f651bb4294 100644 --- a/string-list.c +++ b/string-list.c @@ -200,9 +200,9 @@ void print_string_list(const struct string_list *p, const char *text) { int i; if ( text ) - printf("%s\n", text); + trace_printf("%s\n", text); for (i = 0; i < p->nr; i++) - printf("%s:%p\n", p->items[i].string, p->items[i].util); + trace_printf("%s:%p\n", p->items[i].string, p->items[i].util); } struct string_list_item *string_list_append_nodup(struct string_list *list, -- 2.18.0.597.ga71716f1ad-goog
[PATCH 02/10] string-list.h: add string_list_pop function.
A string list can be used as a stack, but should we? A later patch shows how useful this will be. Signed-off-by: Stefan Beller --- string-list.c | 8 string-list.h | 6 ++ 2 files changed, 14 insertions(+) diff --git a/string-list.c b/string-list.c index 9f651bb4294..ea80afc8a0c 100644 --- a/string-list.c +++ b/string-list.c @@ -80,6 +80,14 @@ void string_list_remove(struct string_list *list, const char *string, } } +struct string_list_item *string_list_pop(struct string_list *list) +{ + if (list->nr == 0) + return 0; + + return >items[--list->nr]; +} + int string_list_has_string(const struct string_list *list, const char *string) { int exact_match; diff --git a/string-list.h b/string-list.h index ff8f6094a33..a1a41bc961a 100644 --- a/string-list.h +++ b/string-list.h @@ -191,6 +191,12 @@ extern void string_list_remove(struct string_list *list, const char *string, */ struct string_list_item *string_list_lookup(struct string_list *list, const char *string); +/** + * Returns the last item inserted and removes it from the list. + * If the list is empty, return NULL. + */ +struct string_list_item *string_list_pop(struct string_list *list); + /* * Remove all but the first of consecutive entries with the same * string value. If free_util is true, call free() on the util -- 2.18.0.597.ga71716f1ad-goog
[RFC PATCH 00/10] fetch: make sure submodule oids are fetched
Currently when git-fetch is asked to recurse into submodules, it dispatches a plain "git-fetch -C " (and some submodule related options such as prefix and recusing strategy, but) without any information of the remote or the tip that should be fetched. This works surprisingly well in some workflows, not so well in others, which this series aims to fix. The first patches provide new basic functionality and do some refactoring; the interesting part is in the two last patches. Thanks, Stefan Stefan Beller (10): string_list: print_string_list to use trace_printf string-list.h: add string_list_pop function. sha1-array: provide oid_array_remove_if submodule.c: convert submodule_move_head new argument to object id submodule.c: fix indentation submodule.c: sort changed_submodule_names before searching it submodule: move global changed_submodule_names into fetch submodule struct submodule.c: do not copy around submodule list submodule: fetch in submodules git directory instead of in worktree fetch: retry fetching submodules if sha1 were not fetched builtin/fetch.c | 9 +- entry.c | 6 +- sha1-array.c| 39 sha1-array.h| 3 + string-list.c | 12 ++- string-list.h | 6 ++ submodule.c | 194 +++- submodule.h | 2 +- t/t5526-fetch-submodules.sh | 23 - unpack-trees.c | 13 +-- 10 files changed, 241 insertions(+), 66 deletions(-) -- 2.18.0.597.ga71716f1ad-goog
Re: Help with "fatal: unable to read ...." error during GC?
On Wed, 2018-08-08 at 14:24 -0400, Jeff King wrote: > Let's narrow it down first and make sure we're dying where I expect. > Can > you try: > > GIT_TRACE=1 git gc > > and confirm the program running when the fatal error is produced? > > From what you've shown it's going to be git-repack, but what I'm not > clear on is whether it is repack itself that is complaining, or the > pack-objects process it spawns. I'd guess the latter. You are correct: 15:27:24.264161 git.c:415 trace: built-in: git pack- objects --keep-true-parents --honor-pack-keep --non-empty --all -- reflog --indexed-objects --unpack-unreachable=2.weeks.ago --local -- delta-base-offset .git/objects/pack/.tmp-17617-pack > If so, can you try running it under gdb and getting a stack trace? I would... but I discovered all my Git binaries are stripped to the max and no symbols available. I'll do a quick rebuild with some debug info and get back to you. Thanks for the pointers!
[PATCH] update-index: there no longer is `apply --index-info`
Back when we removed `git apply --index-info` in 2007, we forgot to adjust the documentation for update-index that reads its output. Let's reorder the description of three formats to present the other two formats that are still generated by git commands before this format, and stop mentioning `git apply --index-info`. Signed-off-by: Junio C Hamano --- Documentation/git-update-index.txt | 15 ++- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 4e8e762e68..c62a683648 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -268,23 +268,20 @@ USING --INDEX-INFO multiple entry definitions from the standard input, and designed specifically for scripts. It can take inputs of three formats: -. mode SP sha1 TAB path -+ -The first format is what "git-apply --index-info" -reports, and used to reconstruct a partial tree -that is used for phony merge base tree when falling -back on 3-way merge. - . mode SP type SP sha1 TAB path + -The second format is to stuff 'git ls-tree' output -into the index file. +This format is to stuff `git ls-tree` output into the index. . mode SP sha1 SP stage TAB path + This format is to put higher order stages into the index file and matches 'git ls-files --stage' output. +. mode SP sha1 TAB path ++ +This format is no longer produced by any Git command, but is +and will continue to be supported by `update-index --index-info`. + To place a higher stage entry to the index, the path should first be removed by feeding a mode=0 entry for the path, and then feeding necessary input lines in the third format.
Re: [PATCH] git-update-index.txt: reword possibly confusing example
Elijah Newren writes: > The following phrase could be interpreted multiple ways: > "To pretend you have a file with mode and sha1 at path" > > In particular, I can think of two: > 1. Pretend we have some new file, which happens to have a given mode > and sha1 > 2. Pretend one of the files we are already tracking has a different > mode and sha1 than what it really does > > I think people could easily assume either case while reading, but the > example command provided doesn't actually handle the first case, which > caused some minor frustration to at least one user. Modify the example > command so that it correctly handles both cases, and re-order the > wording in a way that makes it more likely folks will assume the first > interpretation. I do not think the rephrasing loses those who want to update an existing path, and is a good one. > -To pretend you have a file with mode and sha1 at path, say: > +To pretend you have a file at path with mode and sha1, say: > > > -$ git update-index --cacheinfo ,, > +$ git update-index --add --cacheinfo ,, >
Re: [PATCH v2 0/4] Speed up unpack_trees()
On 8/1/2018 12:38 PM, Duy Nguyen wrote: On Tue, Jul 31, 2018 at 01:31:31PM -0400, Ben Peart wrote: On 7/31/2018 12:50 PM, Ben Peart wrote: On 7/31/2018 11:31 AM, Duy Nguyen wrote: In the performance game of whack-a-mole, that call to repair cache-tree is now looking quite expensive... Yeah and I think we can whack that mole too. I did some measurement. Best case possible, we just need to scan through two indexes (one with many good cache-tree, one with no cache-tree), compare and copy cache-tree over. The scanning takes like 1% time of current repair step and I suspect it's the hashing that takes most of the time. Of course real world won't have such nice numbers, but I guess we could maybe half cache-tree update/repair time. I have some great profiling tools available so will take a look at this next and see exactly where the time is being spent. Good instincts. In cache_tree_update, the heavy hitter is definitely hash_object_file followed by has_object_file. NameInc %Inc + git!cache_tree_update 12.4 4,935 |+ git!update_one11.8 4,706 | + git!update_one 11.8 4,706 | + git!hash_object_file 6.1 2,406 | + git!has_object_file 2.0813 | + OTHER <> 0.5203 | + git!strbuf_addf 0.4155 | + git!strbuf_release 0.4143 | + git!strbuf_add 0.3121 | + OTHER <> 0.2 93 | + git!strbuf_grow 0.1 25 Ben, if you work on this, this could be a good starting point. I will not work on this because I still have some other things to catch up and follow through. You can have my sign off if you reuse something from this patch Even if it's a naive implementation, the initial numbers look pretty good. Without the patch we have 18:31:05.970621 unpack-trees.c:1437 performance: 0.01029 s: copy 18:31:05.975729 unpack-trees.c:1444 performance: 0.005082004 s: update And with the patch 18:31:13.295655 unpack-trees.c:1437 performance: 0.000198017 s: copy 18:31:13.296757 unpack-trees.c:1444 performance: 0.001075935 s: update Time saving is about 80% by the look of this (best possible case because only the top tree needs to be hashed and written out). -- 8< -- diff --git a/cache-tree.c b/cache-tree.c index 6b46711996..67a4a93100 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -440,6 +440,147 @@ int cache_tree_update(struct index_state *istate, int flags) return 0; } +static int same(const struct cache_entry *a, const struct cache_entry *b) +{ + if (ce_stage(a) || ce_stage(b)) + return 0; + if ((a->ce_flags | b->ce_flags) & CE_CONFLICTED) + return 0; + return a->ce_mode == b->ce_mode && + !oidcmp(>oid, >oid); +} + +static int cache_tree_name_pos(const struct index_state *istate, + const struct strbuf *path) +{ + int pos; + + if (!path->len) + return 0; + + pos = index_name_pos(istate, path->buf, path->len); + if (pos >= 0) + BUG("No no no, directory path must not exist in index"); + return -pos - 1; +} + +/* + * Locate the same cache-tree in two separate indexes. Check the + * cache-tree is still valid for the "to" index (i.e. it contains the + * same set of entries in the "from" index). + */ +static int verify_one_cache_tree(const struct index_state *to, +const struct index_state *from, +const struct cache_tree *it, +const struct strbuf *path) +{ + int i, spos, dpos; + + spos = cache_tree_name_pos(from, path); + if (spos + it->entry_count > from->cache_nr) + return -1; + + dpos = cache_tree_name_pos(to, path); + if (dpos + it->entry_count > to->cache_nr) + return -1; + + /* Can we quickly check head and tail and bail out early */ + if (!same(from->cache[spos], to->cache[spos]) || + !same(from->cache[spos + it->entry_count - 1], + to->cache[spos + it->entry_count - 1])) + return -1; + + for (i = 1; i < it->entry_count - 1; i++) + if (!same(from->cache[spos + i], + to->cache[dpos + i])) + return -1; + + return 0; +} + +static int verify_and_invalidate(struct index_state *to, +const struct index_state *from, +struct cache_tree *it, +struct strbuf *path) +{ + /* +* Optimistically verify the current tree first. Alternatively +* we could verify all the subtrees first then do this +* last. Any invalid subtree would also invalidates its +*
[PATCH] git-update-index.txt: reword possibly confusing example
The following phrase could be interpreted multiple ways: "To pretend you have a file with mode and sha1 at path" In particular, I can think of two: 1. Pretend we have some new file, which happens to have a given mode and sha1 2. Pretend one of the files we are already tracking has a different mode and sha1 than what it really does I think people could easily assume either case while reading, but the example command provided doesn't actually handle the first case, which caused some minor frustration to at least one user. Modify the example command so that it correctly handles both cases, and re-order the wording in a way that makes it more likely folks will assume the first interpretation. I believe the new example shouldn't pose any obstacles to those wanting the second interpretation (at worst, they pass an unnecessary extra flag). Signed-off-by: Elijah Newren --- Documentation/git-update-index.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 4e8e762e68..a9753e6557 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -245,10 +245,10 @@ USING --CACHEINFO OR --INFO-ONLY current working directory. This is useful for minimum-checkout merging. -To pretend you have a file with mode and sha1 at path, say: +To pretend you have a file at path with mode and sha1, say: -$ git update-index --cacheinfo ,, +$ git update-index --add --cacheinfo ,, `--info-only` is used to register files without placing them in the object -- 2.18.0.556.g1604670984
Re: [PATCH 0/3] Resending sb/config-write-fix
Stefan Beller writes: > This is a resend of sb/config-write-fix, with a slightly > better commit message and a renamed variable. > > Thanks, > Stefan Thanks. Let's declare victory and mark it to be merged to 'next'.
Re: [GSoC][PATCH v7 05/26] stash: convert apply to builtin
Paul-Sebastian Ungureanu writes: > From: Joel Teichroeb > > Add a builtin helper for performing stash commands. Converting > all at once proved hard to review, so starting with just apply > lets conversion get started without the other commands being > finished. > > The helper is being implemented as a drop in replacement for > stash so that when it is complete it can simply be renamed and > the shell script deleted. > > Delete the contents of the apply_stash shell function and replace > it with a call to stash--helper apply until pop is also > converted. > > Signed-off-by: Joel Teichroeb > Signed-off-by: Paul-Sebastian Ungureanu Good to see that the right way to forward a patch from another person is used, but is this a GSoC project? > diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c > new file mode 100644 > index 0..ef6a9d30d > --- /dev/null > +++ b/builtin/stash--helper.c > @@ -0,0 +1,452 @@ > +#include "builtin.h" > +#include "config.h" > +#include "parse-options.h" > +#include "refs.h" > +#include "lockfile.h" > +#include "cache-tree.h" > +#include "unpack-trees.h" > +#include "merge-recursive.h" > +#include "argv-array.h" > +#include "run-command.h" > +#include "dir.h" > +#include "rerere.h" Wow, "apply" is a biggie, as you'd pretty much have to do everything, like merging and updating the index and asking rerere to auto-resolve. Quite a many include files. > +static const char * const git_stash_helper_usage[] = { > + N_("git stash--helper apply [--index] [-q|--quiet] []"), > + NULL > +}; > + > +static const char * const git_stash_helper_apply_usage[] = { > + N_("git stash--helper apply [--index] [-q|--quiet] []"), > + NULL > +}; > + ... > +static void assert_stash_like(struct stash_info *info, const char * revision) This inherits an unfortunate name from the scripted version (it does more than asserting), but it is OK to keep the original name, especially in this early step in the series. Lose the SP in "* revision"; the asterisk sticks to the variable/parameter name. > +{ > + if (get_oidf(>b_commit, "%s^1", revision) || > + get_oidf(>w_tree, "%s:", revision) || > + get_oidf(>b_tree, "%s^1:", revision) || > + get_oidf(>i_tree, "%s^2:", revision)) { > + free_stash_info(info); > + error(_("'%s' is not a stash-like commit"), revision); > + exit(128); > + } > +} > +static int reset_tree(struct object_id *i_tree, int update, int reset) > +{ > ... > +} Kind-a surprising that there is no helper function to do this already. The implementation looked OK, though. > +static int apply_cached(struct strbuf *out) > +{ > + struct child_process cp = CHILD_PROCESS_INIT; > + > + /* > + * Apply currently only reads either from stdin or a file, thus > + * apply_all_patches would have to be updated to optionally take a > + * buffer. > + */ > + cp.git_cmd = 1; > + argv_array_pushl(, "apply", "--cached", NULL); > + return pipe_command(, out->buf, out->len, NULL, 0, NULL, 0); > +} Applying and updating the index is more resource intensive than spawning a process, and not having to worry about the process dying is a benefit, so overall, making this into an internal call would be a lot lower priority, I would guess. > +static int reset_head(const char *prefix) > +{ This is resetting the index to the HEAD, right? reset_head sounds as if it takes a commit-ish and moves HEAD there.
Re: [PATCH 1/1] pull --rebase=: allow single-letter abbreviations for the type
Ævar Arnfjörð Bjarmason writes: >> -else if (!strcmp(value, "preserve")) >> +else if (!strcmp(value, "preserve") || !strcmp(value, "p")) >> return REBASE_PRESERVE; >> -else if (!strcmp(value, "merges")) >> +else if (!strcmp(value, "merges") || !strcmp(value, "m")) >> return REBASE_MERGES; >> -else if (!strcmp(value, "interactive")) >> +else if (!strcmp(value, "interactive") || !strcmp(value, "i")) >> return REBASE_INTERACTIVE; > > Here 3 special cases are added... > ... >> +test_expect_success 'pull --rebase=i' ' >> ... >> +' >> + >> test_expect_success 'pull.rebase=invalid fails' ' >> git reset --hard before-preserve-rebase && >> test_config pull.rebase invalid && > > ...but this test is only for 1/3. I haven't run this, but it looks like > the tests will still pass if we remove --rebase=p and --rebase=m. Good eyes. It's not like that parsing these three is implemented with one thing; in other words, it is not hard to break one without breaking the other two.
[PATCH 2/3] config: fix case sensitive subsection names on writing
A user reported a submodule issue regarding a section mix-up, but it could be boiled down to the following test case: $ git init test && cd test $ git config foo."Bar".key test $ git config foo."bar".key test $ tail -n 3 .git/config [foo "Bar"] key = test key = test Sub sections are case sensitive and we have a test for correctly reading them. However we do not have a test for writing out config correctly with case sensitive subsection names, which is why this went unnoticed in 6ae996f2acf (git_config_set: make use of the config parser's event stream, 2018-04-09) Unfortunately we have to make a distinction between old style configuration that looks like [foo.Bar] key = test and the new quoted style as seen above. The old style is documented as case-agnostic, hence we need to keep 'strncasecmp'; although the resulting setting for the old style config differs from the configuration. That will be fixed in a follow up patch. Reported-by: JP Sugarbroad Signed-off-by: Stefan Beller --- config.c | 12 +++- t/t1300-config.sh | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 02050529788..27e800c7ce8 100644 --- a/config.c +++ b/config.c @@ -35,6 +35,7 @@ struct config_source { int eof; struct strbuf value; struct strbuf var; + unsigned subsection_case_sensitive : 1; int (*do_fgetc)(struct config_source *c); int (*do_ungetc)(int c, struct config_source *conf); @@ -603,6 +604,7 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name) static int get_extended_base_var(struct strbuf *name, int c) { + cf->subsection_case_sensitive = 0; do { if (c == '\n') goto error_incomplete_line; @@ -639,6 +641,7 @@ static int get_extended_base_var(struct strbuf *name, int c) static int get_base_var(struct strbuf *name) { + cf->subsection_case_sensitive = 1; for (;;) { int c = get_next_char(); if (cf->eof) @@ -2328,14 +2331,21 @@ static int store_aux_event(enum config_event_t type, store->parsed[store->parsed_nr].type = type; if (type == CONFIG_EVENT_SECTION) { + int (*cmpfn)(const char *, const char *, size_t); + if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.') return error("invalid section name '%s'", cf->var.buf); + if (cf->subsection_case_sensitive) + cmpfn = strncasecmp; + else + cmpfn = strncmp; + /* Is this the section we were looking for? */ store->is_keys_section = store->parsed[store->parsed_nr].is_keys_section = cf->var.len - 1 == store->baselen && - !strncasecmp(cf->var.buf, store->key, store->baselen); + !cmpfn(cf->var.buf, store->key, store->baselen); if (store->is_keys_section) { store->section_seen = 1; ALLOC_GROW(store->seen, store->seen_nr + 1, diff --git a/t/t1300-config.sh b/t/t1300-config.sh index c15c19bf78d..77c5899d000 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1260,6 +1260,7 @@ test_expect_success 'setting different case sensitive subsections ' ' Qc = v2 [d "e"] f = v1 + [d "E"] Qf = v2 EOF # exact match -- 2.18.0.597.ga71716f1ad-goog
[PATCH 1/3] t1300: document current behavior of setting options
This documents current behavior of the config machinery, when changing the value of some settings. This patch just serves to provide a baseline for the follow up that will fix some issues with the current behavior. Signed-off-by: Stefan Beller --- t/t1300-config.sh | 86 +++ 1 file changed, 86 insertions(+) diff --git a/t/t1300-config.sh b/t/t1300-config.sh index e43982a9c1f..c15c19bf78d 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -1188,6 +1188,92 @@ test_expect_success 'last one wins: three level vars' ' test_cmp expect actual ' +test_expect_success 'old-fashioned settings are case insensitive' ' + test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" && + + cat >testConfig_actual <<-EOF && + [V.A] + r = value1 + EOF + q_to_tab >testConfig_expect <<-EOF && + [V.A] + Qr = value2 + EOF + git config -f testConfig_actual "v.a.r" value2 && + test_cmp testConfig_expect testConfig_actual && + + cat >testConfig_actual <<-EOF && + [V.A] + r = value1 + EOF + q_to_tab >testConfig_expect <<-EOF && + [V.A] + QR = value2 + EOF + git config -f testConfig_actual "V.a.R" value2 && + test_cmp testConfig_expect testConfig_actual && + + cat >testConfig_actual <<-EOF && + [V.A] + r = value1 + EOF + q_to_tab >testConfig_expect <<-EOF && + [V.A] + r = value1 + Qr = value2 + EOF + git config -f testConfig_actual "V.A.r" value2 && + test_cmp testConfig_expect testConfig_actual && + + cat >testConfig_actual <<-EOF && + [V.A] + r = value1 + EOF + q_to_tab >testConfig_expect <<-EOF && + [V.A] + r = value1 + Qr = value2 + EOF + git config -f testConfig_actual "v.A.r" value2 && + test_cmp testConfig_expect testConfig_actual +' + +test_expect_success 'setting different case sensitive subsections ' ' + test_when_finished "rm -f testConfig testConfig_expect testConfig_actual" && + + cat >testConfig_actual <<-EOF && + [V "A"] + R = v1 + [K "E"] + Y = v1 + [a "b"] + c = v1 + [d "e"] + f = v1 + EOF + q_to_tab >testConfig_expect <<-EOF && + [V "A"] + Qr = v2 + [K "E"] + Qy = v2 + [a "b"] + Qc = v2 + [d "e"] + f = v1 + Qf = v2 + EOF + # exact match + git config -f testConfig_actual a.b.c v2 && + # match section and subsection, key is cased differently. + git config -f testConfig_actual K.E.y v2 && + # section and key are matched case insensitive, but subsection needs + # to match; When writing out new values only the key is adjusted + git config -f testConfig_actual v.A.r v2 && + # subsection is not matched: + git config -f testConfig_actual d.E.f v2 && + test_cmp testConfig_expect testConfig_actual +' + for VAR in a .a a. a.0b a."b c". a."b c".0d do test_expect_success "git -c $VAR=VAL rejects invalid '$VAR'" ' -- 2.18.0.597.ga71716f1ad-goog
[PATCH 3/3] git-config: document accidental multi-line setting in deprecated syntax
The bug was noticed when writing the previous patch; a fix for this bug is not easy though: If we choose to ignore the case of the subsection (and revert most of the code of the previous patch, just keeping s/strncasecmp/strcmp/), then we'd introduce new sections using the new syntax, such that [section.subsection] key = value1 git config section.Subsection.key value2 would result in [section.subsection] key = value1 [section.Subsection] key = value2 which is even more confusing. A proper fix would replace the first occurrence of 'key'. As the syntax is deprecated, let's prefer to not spend time on fixing the behavior and just document it instead. Signed-off-by: Stefan Beller --- Documentation/git-config.txt | 21 + 1 file changed, 21 insertions(+) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 14da5fc157e..1ac2eab9482 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -430,6 +430,27 @@ http.sslverify false include::config.txt[] +BUGS + +When using the deprecated `[section.subsection]` syntax, changing a value +will result in adding a multi-line key instead of a change, if the subsection +is given with at least one uppercase character. For example when the config +looks like + + + [section.subsection] +key = value1 + + +and running `git config section.Subsection.key value2` will result in + + + [section.subsection] +key = value1 +key = value2 + + + GIT --- Part of the linkgit:git[1] suite -- 2.18.0.597.ga71716f1ad-goog
[PATCH 0/3] Resending sb/config-write-fix
This is a resend of sb/config-write-fix, with a slightly better commit message and a renamed variable. Thanks, Stefan Stefan Beller (3): t1300: document current behavior of setting options config: fix case sensitive subsection names on writing git-config: document accidental multi-line setting in deprecated syntax Documentation/git-config.txt | 21 + config.c | 12 - t/t1300-config.sh| 87 3 files changed, 119 insertions(+), 1 deletion(-) ./git-range-diff origin/sb/config-write-fix...HEAD >>-cover-letter.patch 2.18.0.597.ga71716f1ad-goog 1: 999d9026272 ! 1: e40f57f3da1 t1300: document current behavior of setting options @@ -7,7 +7,6 @@ for the follow up that will fix some issues with the current behavior. Signed-off-by: Stefan Beller -Signed-off-by: Junio C Hamano diff --git a/t/t1300-config.sh b/t/t1300-config.sh --- a/t/t1300-config.sh 2: c667e555066 ! 2: f01cb1d9dae config: fix case sensitive subsection names on writing @@ -2,8 +2,8 @@ config: fix case sensitive subsection names on writing -A use reported a submodule issue regarding strange case indentation -issues, but it could be boiled down to the following test case: +A user reported a submodule issue regarding a section mix-up, +but it could be boiled down to the following test case: $ git init test && cd test $ git config foo."Bar".key test @@ -32,7 +32,6 @@ Reported-by: JP Sugarbroad Signed-off-by: Stefan Beller -Signed-off-by: Junio C Hamano diff --git a/config.c b/config.c --- a/config.c @@ -41,7 +40,7 @@ int eof; struct strbuf value; struct strbuf var; -+ unsigned section_name_old_dot_style : 1; ++ unsigned subsection_case_sensitive : 1; int (*do_fgetc)(struct config_source *c); int (*do_ungetc)(int c, struct config_source *conf); @@ -49,7 +48,7 @@ static int get_extended_base_var(struct strbuf *name, int c) { -+ cf->section_name_old_dot_style = 0; ++ cf->subsection_case_sensitive = 0; do { if (c == '\n') goto error_incomplete_line; @@ -57,7 +56,7 @@ static int get_base_var(struct strbuf *name) { -+ cf->section_name_old_dot_style = 1; ++ cf->subsection_case_sensitive = 1; for (;;) { int c = get_next_char(); if (cf->eof) @@ -70,7 +69,7 @@ if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.') return error("invalid section name '%s'", cf->var.buf); -+ if (cf->section_name_old_dot_style) ++ if (cf->subsection_case_sensitive) + cmpfn = strncasecmp; + else + cmpfn = strncmp; 3: 6749bb283a8 ! 3: 6b5ad773490 git-config: document accidental multi-line setting in deprecated syntax @@ -29,7 +29,6 @@ spend time on fixing the behavior and just document it instead. Signed-off-by: Stefan Beller -Signed-off-by: Junio C Hamano diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt --- a/Documentation/git-config.txt
Re: [PATCH v2] clone: report duplicate entries on case-insensitive filesystems
On 8/7/2018 3:31 PM, Junio C Hamano wrote: Nguyễn Thái Ngọc Duy writes: One nice thing about this is we don't need platform specific code for detecting the duplicate entries. I think ce_match_stat() works even on Windows. And it's now equally expensive on all platforms :D ce_match_stat() may not be a very good measure to see if two paths refer to the same file, though. After a fresh checkout, I would not be surprised if two completely unrelated paths have the same size and have same mtime/ctime. In its original use case, i.e. "I have one specific path in mind and took a snapshot of its metadata earlier. Is it still the same, or has it been touched?", that may be sufficient to detect that the path has been _modified_, but without reliable inum, it may be a poor measure to say two paths refer to the same. I agree with Junio on this one. The mtime values are sloppy at best. On FAT file systems, they have 2 second resolution. Even NTFS IIRC has only 100ns resolution, so we might get a lot of false matches using this technique, right? It might be better to build an equivalence-class hash-map for the colliding entries. Compute a "normalized" version of the pathname (such as convert to lowercase, strip final-dots/spaces, strip the digits following tilda of a shortname, and etc for the MAC's UTF-isms). Then when you rescan the index entries to find the matches, apply the equivalence operator on the pathname and do the hashmap lookup. When you find a match, you have a "potential" collider pair (I say potential only because of the ambiguity of shortnames). Then we can use inum/file-index/whatever to see if they actually collide. Jeff
Re: [PATCH 04/11] builtin rebase: support --quiet
Stefan Beller writes: > On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki wrote: >> >> This commit introduces a rebase option `--quiet`. While `--quiet` is >> commonly perceived as opposite to `--verbose`, this is not the case for >> the rebase command: both `--quiet` and `--verbose` default to `false` if >> neither `--quiet` nor `--verbose` is present. >> >> This commit goes further and introduces `--no-quiet` which is the >> contrary of `--quiet` and it's introduction doesn't modify any >> behaviour. Why? Is it for completeness (i.e. does the scripted version take such an option and addition of --no-quiet makes the C rewrite behave the same)? >> Note: The `flags` field in `rebase_options` will accumulate more bits in >> subsequent commits, in particular a verbose and a diffstat flag. And as >> --quoet inthe shell scripted version of the rebase command switches off > > --quote in the > > (in case a resend is needed) Meaning --quiet?
Re: [PATCH 03/11] builtin rebase: handle the pre-rebase hook (and add --no-verify)
Pratik Karki writes: > This commit converts the equivalent part of the shell script > `git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and > to interrupt the rebase with error if the hook fails. > > Signed-off-by: Pratik Karki > --- Introduction of upstream_arg in this step looked a bit surprising, but the hook invocation is the only thing that uses it, so it is understandable. "rebase: handle the re-rebase hook and --no-verify" would have been sufficient, without "add" or parentheses. > builtin/rebase.c | 11 +++ > 1 file changed, 11 insertions(+) > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index 38c496dd10..b79f9b0a9f 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -70,6 +70,7 @@ struct rebase_options { > const char *state_dir; > struct commit *upstream; > const char *upstream_name; > + const char *upstream_arg; > char *head_name; > struct object_id orig_head; > struct commit *onto; > @@ -310,6 +311,7 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > }; > const char *branch_name; > int ret, flags; > + int ok_to_skip_pre_rebase = 0; > struct strbuf msg = STRBUF_INIT; > struct strbuf revisions = STRBUF_INIT; > struct object_id merge_base; > @@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > OPT_STRING(0, "onto", _name, > N_("revision"), > N_("rebase onto given branch instead of upstream")), > + OPT_BOOL(0, "no-verify", _to_skip_pre_rebase, > + N_("allow pre-rebase hook to run")), Do we need to catch "--no-no-verify" ourselves with NONEG bit, or is this sufficient to tell parse_options() machinery to take care of it? > OPT_END(), > }; > > @@ -382,6 +386,7 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > options.upstream = peel_committish(options.upstream_name); > if (!options.upstream) > die(_("invalid upstream '%s'"), options.upstream_name); > + options.upstream_arg = options.upstream_name; > } else > die("TODO: upstream for --root"); > > @@ -430,6 +435,12 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > die(_("Could not resolve HEAD to a revision")); > } > > + /* If a hook exists, give it a chance to interrupt*/ > + if (!ok_to_skip_pre_rebase && > + run_hook_le(NULL, "pre-rebase", options.upstream_arg, > + argc ? argv[0] : NULL, NULL)) > + die(_("The pre-rebase hook refused to rebase.")); > + > strbuf_addf(, "rebase: checkout %s", options.onto_name); > if (reset_head(>object.oid, "checkout", NULL, 1)) > die(_("Could not detach HEAD"));
Re: [PATCH 02/11] builtin rebase: support `git rebase --onto A...B`
Pratik Karki writes: > This commit implements support for an --onto argument that is actually a > "symmetric range" i.e. `...`. > > The equivalent shell script version of the code offers two different > error messages for the cases where there is no merge base vs more than > one merge base. Though following the similar approach would be nice, > this would create more complexity than it is of current. Currently, for Sorry, but it is unclear what you mean by "than it is of current." Do you mean we leave it broken at this step in the series for now for expediency, with the intention to later revisit and fix it, or do you mean something else? > simple convenience, the `get_oid_mb()` function is used whose return > value does not discern between those two error conditions. > > Signed-off-by: Pratik Karki > ... > @@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > if (!options.onto_name) > options.onto_name = options.upstream_name; > if (strstr(options.onto_name, "...")) { > - die("TODO"); > + if (get_oid_mb(options.onto_name, _base) < 0) > + die(_("'%s': need exactly one merge base"), > + options.onto_name); > + options.onto = lookup_commit_or_die(_base, > + options.onto_name); The original is slightly sloppy in that it will misparse rebase --onto 'master^{/log ... message}' and this shares the same, which I think is probably OK. When this actually becomes problematic, the original can easily be salvaged by making it to fall back to the same peel_committish in its else clause; I am not sure if this C rewrite is as easily be fixed the same way, though. > } else { > options.onto = peel_committish(options.onto_name); > if (!options.onto)
Re: [PATCH 01/11] builtin rebase: support --onto
Pratik Karki writes: > The `--onto` option is important, as it allows to rebase a range of > commits onto a different base commit (which gave the command its odd > name: "rebase"). Is there anything unimportant? A rhetorical question, of course. The quite casual and natural use of "to rebase" as a verb in the first sentence contradicts with what the parenthetical "its odd name" comment says. Perhaps drop everything after "(which..."? i.e. The `--onto` option allows to rebase a range of commits onto a different base commit. Port the support for the option to the C re-implementation. > This commit introduces options parsing so that different options can > be added in future commits. We usually do not say "This commit does X", or (worse) "I do X in this commit". Instead, order the codebase to be like so, e.g. "Support command line options by adding a call to parse_options(); later commits will add more options by building on top." or something like that. > @@ -318,13 +334,22 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > BUG("sane_execvp() returned???"); > } > > - if (argc != 2) > - die(_("Usage: %s "), argv[0]); > + if (argc == 2 && !strcmp(argv[1], "-h")) > + usage_with_options(builtin_rebase_usage, > +builtin_rebase_options); > + > prefix = setup_git_directory(); > trace_repo_setup(prefix); > setup_work_tree(); > > git_config(git_default_config, NULL); > + argc = parse_options(argc, argv, prefix, > + builtin_rebase_options, > + builtin_rebase_usage, 0); > + > + if (argc > 2) > + usage_with_options(builtin_rebase_usage, > +builtin_rebase_options); OK. This correctly calls the parser after repository setup. > switch (options.type) { > case REBASE_MERGE: > @@ -343,10 +368,10 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > } > > if (!options.root) { > - if (argc < 2) > + if (argc < 1) > die("TODO: handle @{upstream}"); > else { > - options.upstream_name = argv[1]; > + options.upstream_name = argv[0]; > argc--; > argv++; > if (!strcmp(options.upstream_name, "-")) > @@ -377,7 +402,7 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) >* orig_head -- commit object name of tip of the branch before rebasing >* head_name -- refs/heads/ or "detached HEAD" >*/ > - if (argc > 1) > + if (argc > 0) >die("TODO: handle switch_to"); > else { > /* Do not need to switch branches, we are already on it. */
Re: Page content is wider than view window
Jeff King writes: > Yes, there's work happening already for a visual refresh of the site, > [...] > > -Peff Awesome! Looking forward to it! -- Brady
[GSoC][PATCH v7 11/26] stash: change `git stash show` usage text and documentation
It is already stated in documentation that it will accept any option known to `git diff`, but not in the usage text and some parts of the documentation. Signed-off-by: Paul-Sebastian Ungureanu --- Documentation/git-stash.txt | 4 ++-- builtin/stash--helper.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 7ef8c4791..e31ea7d30 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -9,7 +9,7 @@ SYNOPSIS [verse] 'git stash' list [] -'git stash' show [] +'git stash' show [] [] 'git stash' drop [-q|--quiet] [] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [] 'git stash' branch [] @@ -106,7 +106,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1]. -show []:: +show [] []:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index e764cd33e..0c1efca6b 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -13,7 +13,7 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper list []"), - N_("git stash--helper show []"), + N_("git stash--helper show [] []"), N_("git stash--helper drop [-q|--quiet] []"), N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] []"), N_("git stash--helper branch []"), @@ -27,7 +27,7 @@ static const char * const git_stash_helper_list_usage[] = { }; static const char * const git_stash_helper_show_usage[] = { - N_("git stash--helper show []"), + N_("git stash--helper show [] []"), NULL }; -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 07/26] stash: convert branch to builtin
From: Joel Teichroeb Add stash branch to the helper and delete the apply_to_branch function from the shell script. Checkout does not currently provide a function for checking out a branch as cmd_checkout does a large amount of sanity checks first that we require here. Signed-off-by: Joel Teichroeb Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 44 + git-stash.sh| 17 ++-- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index ae719b7fc..1e4d07295 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -14,6 +14,7 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper drop [-q|--quiet] []"), N_("git stash--helper apply [--index] [-q|--quiet] []"), + N_("git stash--helper branch []"), N_("git stash--helper clear"), NULL }; @@ -28,6 +29,11 @@ static const char * const git_stash_helper_apply_usage[] = { NULL }; +static const char * const git_stash_helper_branch_usage[] = { + N_("git stash--helper branch []"), + NULL +}; + static const char * const git_stash_helper_clear_usage[] = { N_("git stash--helper clear"), NULL @@ -535,6 +541,42 @@ static int drop_stash(int argc, const char **argv, const char *prefix) return ret; } +static int branch_stash(int argc, const char **argv, const char *prefix) +{ + const char *branch = NULL; + int ret; + struct child_process cp = CHILD_PROCESS_INIT; + struct stash_info info; + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_branch_usage, 0); + + if (argc == 0) + return error(_("No branch name specified")); + + branch = argv[0]; + + if (get_stash_info(, argc - 1, argv + 1)) + return -1; + + cp.git_cmd = 1; + argv_array_pushl(, "checkout", "-b", NULL); + argv_array_push(, branch); + argv_array_push(, oid_to_hex(_commit)); + ret = run_command(); + if (!ret) + ret = do_apply_stash(prefix, , 1); + if (!ret && info.is_stash_ref) + ret = do_drop_stash(prefix, ); + + free_stash_info(); + + return ret; +} + int cmd_stash__helper(int argc, const char **argv, const char *prefix) { pid_t pid = getpid(); @@ -561,6 +603,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) return !!clear_stash(argc, argv, prefix); else if (!strcmp(argv[0], "drop")) return !!drop_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "branch")) + return !!branch_stash(argc, argv, prefix); usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_helper_usage, options); diff --git a/git-stash.sh b/git-stash.sh index a99d5dc9e..29d9f4425 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -598,20 +598,6 @@ drop_stash () { clear_stash } -apply_to_branch () { - test -n "$1" || die "$(gettext "No branch name specified")" - branch=$1 - shift 1 - - set -- --index "$@" - assert_stash_like "$@" - - git checkout -b $branch $REV^ && - apply_stash "$@" && { - test -z "$IS_STASH_REF" || drop_stash "$@" - } -} - test "$1" = "-p" && set "push" "$@" PARSE_CACHE='--not-parsed' @@ -673,7 +659,8 @@ pop) ;; branch) shift - apply_to_branch "$@" + cd "$START_DIR" + git stash--helper branch "$@" ;; *) case $# in -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 03/26] stash: update test cases conform to coding guidelines
Removed whitespaces after redirection operators. Signed-off-by: Paul-Sebastian Ungureanu --- t/t3903-stash.sh | 120 --- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index af7586d43..de6cab1fe 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -8,22 +8,22 @@ test_description='Test git stash' . ./test-lib.sh test_expect_success 'stash some dirty working directory' ' - echo 1 > file && + echo 1 >file && git add file && echo unrelated >other-file && git add other-file && test_tick && git commit -m initial && - echo 2 > file && + echo 2 >file && git add file && - echo 3 > file && + echo 3 >file && test_tick && git stash && git diff-files --quiet && git diff-index --cached --quiet HEAD ' -cat > expect << EOF +cat >expect < output && + git diff stash^2..stash >output && test_cmp output expect ' @@ -74,7 +74,7 @@ test_expect_success 'apply stashed changes' ' test_expect_success 'apply stashed changes (including index)' ' git reset --hard HEAD^ && - echo 6 > other-file && + echo 6 >other-file && git add other-file && test_tick && git commit -m other-file && @@ -99,12 +99,12 @@ test_expect_success 'stash drop complains of extra options' ' test_expect_success 'drop top stash' ' git reset --hard && - git stash list > stashlist1 && - echo 7 > file && + git stash list >expected && + echo 7 >file && git stash && git stash drop && - git stash list > stashlist2 && - test_cmp stashlist1 stashlist2 && + git stash list >actual && + test_cmp expected actual && git stash apply && test 3 = $(cat file) && test 1 = $(git show :file) && @@ -113,9 +113,9 @@ test_expect_success 'drop top stash' ' test_expect_success 'drop middle stash' ' git reset --hard && - echo 8 > file && + echo 8 >file && git stash && - echo 9 > file && + echo 9 >file && git stash && git stash drop stash@{1} && test 2 = $(git stash list | wc -l) && @@ -160,7 +160,7 @@ test_expect_success 'stash pop' ' test 0 = $(git stash list | wc -l) ' -cat > expect << EOF +cat >expect < expect1 << EOF +cat >expect1 < expect2 << EOF +cat >expect2 < file && + echo foo >file && git commit file -m first && - echo bar > file && - echo bar2 > file2 && + echo bar >file && + echo bar2 >file2 && git add file2 && git stash && - echo baz > file && + echo baz >file && git commit file -m second && git stash branch stashbranch && test refs/heads/stashbranch = $(git symbolic-ref HEAD) && test $(git rev-parse HEAD) = $(git rev-parse master^) && - git diff --cached > output && + git diff --cached >output && test_cmp output expect && - git diff > output && + git diff >output && test_cmp output expect1 && git add file && git commit -m alternate\ second && - git diff master..stashbranch > output && + git diff master..stashbranch >output && test_cmp output expect2 && test 0 = $(git stash list | wc -l) ' test_expect_success 'apply -q is quiet' ' - echo foo > file && + echo foo >file && git stash && - git stash apply -q > output.out 2>&1 && + git stash apply -q >output.out 2>&1 && test_must_be_empty output.out ' test_expect_success 'save -q is quiet' ' - git stash save --quiet > output.out 2>&1 && + git stash save --quiet >output.out 2>&1 && test_must_be_empty output.out ' test_expect_success 'pop -q is quiet' ' - git stash pop -q > output.out 2>&1 && + git stash pop -q >output.out 2>&1 && test_must_be_empty output.out ' test_expect_success 'pop -q --index works and is quiet' ' - echo foo > file && + echo foo >file && git add file && git stash save --quiet && - git stash pop -q --index > output.out 2>&1 && + git stash pop -q --index >output.out 2>&1 && test foo = "$(git show :file)" && test_must_be_empty output.out ' test_expect_success 'drop -q is quiet' ' git stash && - git stash drop -q > output.out 2>&1 && + git stash drop -q >output.out 2>&1 && test_must_be_empty output.out ' test_expect_success 'stash -k' ' - echo bar3 > file && - echo bar4 > file2 && + echo bar3 >file && + echo bar4 >file2 && git add file2 && git stash -k && test bar,bar4 = $(cat file),$(cat file2) ' test_expect_success 'stash --no-keep-index' ' - echo bar33 > file && - echo bar44 > file2 && + echo bar33 >file && + echo
[GSoC][PATCH v7 17/26] stash: avoid spawning a "diff-index" process
This commits replaces spawning `diff-index` child process by using the already existing `diff` API --- builtin/stash--helper.c | 56 ++--- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index 887b78d05..f905d3908 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -12,6 +12,7 @@ #include "rerere.h" #include "revision.h" #include "log-tree.h" +#include "diffcore.h" static const char * const git_stash_helper_usage[] = { N_("git stash--helper list []"), @@ -297,6 +298,18 @@ static int reset_head(const char *prefix) return run_command(); } +static void add_diff_to_buf(struct diff_queue_struct *q, + struct diff_options *options, + void *data) +{ + int i; + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + strbuf_addstr(data, p->one->path); + strbuf_addch(data, '\n'); + } +} + static int get_newly_staged(struct strbuf *out, struct object_id *c_tree) { struct child_process cp = CHILD_PROCESS_INIT; @@ -981,14 +994,16 @@ static int stash_patch(struct stash_info *info, const char **argv) return ret; } -static int stash_working_tree(struct stash_info *info, const char **argv) +static int stash_working_tree(struct stash_info *info, + const char **argv, const char *prefix) { int ret = 0; - struct child_process cp1 = CHILD_PROCESS_INIT; struct child_process cp2 = CHILD_PROCESS_INIT; struct child_process cp3 = CHILD_PROCESS_INIT; - struct strbuf out1 = STRBUF_INIT; struct strbuf out3 = STRBUF_INIT; + struct argv_array args = ARGV_ARRAY_INIT; + struct strbuf diff_output = STRBUF_INIT; + struct rev_info rev; set_alternate_index_output(stash_index_path.buf); if (reset_tree(>i_tree, 0, 0)) { @@ -997,26 +1012,36 @@ static int stash_working_tree(struct stash_info *info, const char **argv) } set_alternate_index_output(".git/index"); - cp1.git_cmd = 1; - argv_array_pushl(, "diff-index", "--name-only", "-z", - "HEAD", "--", NULL); + argv_array_push(, "dummy"); if (argv) - argv_array_pushv(, argv); - argv_array_pushf(_array, "GIT_INDEX_FILE=%s", -stash_index_path.buf); + argv_array_pushv(, argv); + git_config(git_diff_basic_config, NULL); + init_revisions(, prefix); + args.argc = setup_revisions(args.argc, args.argv, , NULL); + + rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = add_diff_to_buf; + rev.diffopt.format_callback_data = _output; + + if (read_cache_preload() < 0) { + ret = -1; + goto done; + } - if (pipe_command(, NULL, 0, , 0, NULL, 0)) { + add_pending_object(, parse_object(the_repository, >b_commit), ""); + if (run_diff_index(, 0)) { ret = -1; goto done; } cp2.git_cmd = 1; - argv_array_pushl(, "update-index", "-z", "--add", + argv_array_pushl(, "update-index", "--add", "--remove", "--stdin", NULL); argv_array_pushf(_array, "GIT_INDEX_FILE=%s", stash_index_path.buf); - if (pipe_command(, out1.buf, out1.len, NULL, 0, NULL, 0)) { + if (pipe_command(, diff_output.buf, diff_output.len, +NULL, 0, NULL, 0)) { ret = -1; goto done; } @@ -1033,8 +1058,11 @@ static int stash_working_tree(struct stash_info *info, const char **argv) get_oid_hex(out3.buf, >w_tree); done: - strbuf_release(); + UNLEAK(rev); strbuf_release(); + argv_array_clear(); + object_array_clear(); + strbuf_release(_output); remove_path(stash_index_path.buf); return ret; } @@ -1112,7 +1140,7 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, goto done; } } else { - if (stash_working_tree(info, argv)) { + if (stash_working_tree(info, argv, prefix)) { printf_ln("Cannot save the current worktree state"); ret = -1; goto done; -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 15/26] stash: convert create to builtin
Add stash create to the helper. Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 406 git-stash.sh| 2 +- 2 files changed, 407 insertions(+), 1 deletion(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index 5ff810f8c..a4e57899b 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -21,6 +21,7 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper branch []"), N_("git stash--helper clear"), N_("git stash--helper store [-m|--message ] [-q|--quiet] "), + N_("git stash--helper create []"), NULL }; @@ -64,6 +65,11 @@ static const char * const git_stash_helper_store_usage[] = { NULL }; +static const char * const git_stash_helper_create_usage[] = { + N_("git stash--helper create []"), + NULL +}; + static const char *ref_stash = "refs/stash"; static int quiet; static struct strbuf stash_index_path = STRBUF_INIT; @@ -781,6 +787,404 @@ static int store_stash(int argc, const char **argv, const char *prefix) return do_store_stash(argv[0], stash_msg, quiet); } +/* + * `out` will be filled with the names of untracked files. The return value is: + * + * < 0 if there was a bug (any arg given outside the repo will be detected + * by `setup_revision()`) + * = 0 if there are not any untracked files + * > 0 if there are untracked files + */ +static int get_untracked_files(const char **argv, int line_term, + int include_untracked, struct strbuf *out) +{ + struct child_process cp = CHILD_PROCESS_INIT; + cp.git_cmd = 1; + argv_array_pushl(, "ls-files", "-o", NULL); + if (line_term) + argv_array_push(, "-z"); + if (include_untracked != 2) + argv_array_push(, "--exclude-standard"); + argv_array_push(, "--"); + if (argv) + argv_array_pushv(, argv); + + if (pipe_command(, NULL, 0, out, 0, NULL, 0)) + return -1; + return out->len; +} + +/* + * The return value of `check_changes()` can be: + * + * < 0 if there was an error + * = 0 if there are no changes. + * > 0 if there are changes. + */ +static int check_changes(const char **argv, int include_untracked, +const char *prefix) +{ + int result; + int ret = 0; + struct rev_info rev; + struct object_id dummy; + struct strbuf out = STRBUF_INIT; + struct argv_array args = ARGV_ARRAY_INIT; + + init_revisions(, prefix); + parse_pathspec(_data, 0, PATHSPEC_PREFER_FULL, + prefix, argv); + + rev.diffopt.flags.quick = 1; + rev.diffopt.flags.ignore_submodules = 1; + rev.abbrev = 0; + + /* No initial commit. */ + if (get_oid("HEAD", )) { + ret = -1; + goto done; + } + + add_head_to_pending(); + diff_setup_done(); + + if (read_cache() < 0) { + ret = -1; + goto done; + } + result = run_diff_index(, 1); + if (diff_result_code(, result)) { + ret = 1; + goto done; + } + + object_array_clear(); + result = run_diff_files(, 0); + if (diff_result_code(, result)) { + ret = 1; + goto done; + } + + if (include_untracked && get_untracked_files(argv, 0, +include_untracked, )) + ret = 1; + +done: + strbuf_release(); + argv_array_clear(); + return ret; +} + +static int save_untracked_files(struct stash_info *info, struct strbuf *msg, + struct strbuf *out) +{ + int ret = 0; + struct strbuf untracked_msg = STRBUF_INIT; + struct strbuf out2 = STRBUF_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + struct child_process cp2 = CHILD_PROCESS_INIT; + + cp.git_cmd = 1; + argv_array_pushl(, "update-index", "-z", "--add", +"--remove", "--stdin", NULL); + argv_array_pushf(_array, "GIT_INDEX_FILE=%s", +stash_index_path.buf); + + strbuf_addf(_msg, "untracked files on %s\n", msg->buf); + if (pipe_command(, out->buf, out->len, NULL, 0, NULL, 0)) { + ret = -1; + goto done; + } + + cp2.git_cmd = 1; + argv_array_push(, "write-tree"); + argv_array_pushf(_array, "GIT_INDEX_FILE=%s", +stash_index_path.buf); + if (pipe_command(, NULL, 0, , 0,NULL, 0)) { + ret = -1; + goto done; + } + get_oid_hex(out2.buf, >u_tree); + + if (commit_tree(untracked_msg.buf, untracked_msg.len, + >u_tree, NULL, >u_commit, NULL, NULL)) { + ret = -1; + goto done; + } + +done: +
[GSoC][PATCH v7 26/26] stash: replace all "git apply" child processes with API calls
`apply_all_patches()` does not provide a method to apply patches from strbuf. Because of this, this commit introduces a new function `apply_patch_from_buf()` which applies a patch from buf. It works by saving the strbuf as a file. This way we can call `apply_all_patches()`. Before returning, the created file is removed. --- builtin/stash.c | 61 +++-- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index 46e76a34e..74eda822c 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -13,6 +13,7 @@ #include "revision.h" #include "log-tree.h" #include "diffcore.h" +#include "apply.h" static const char * const git_stash_usage[] = { N_("git stash list []"), @@ -277,10 +278,6 @@ static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit) struct child_process cp = CHILD_PROCESS_INIT; const char *w_commit_hex = oid_to_hex(w_commit); - /* -* Diff-tree would not be very hard to replace with a native function, -* however it should be done together with apply_cached. -*/ cp.git_cmd = 1; argv_array_pushl(, "diff-tree", "--binary", NULL); argv_array_pushf(, "%s^2^..%s^2", w_commit_hex, w_commit_hex); @@ -288,18 +285,36 @@ static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit) return pipe_command(, NULL, 0, out, 0, NULL, 0); } -static int apply_cached(struct strbuf *out) +static int apply_patch_from_buf(struct strbuf *patch, int cached, int reverse, + int check_index) { - struct child_process cp = CHILD_PROCESS_INIT; + int ret = 0; + struct apply_state state; + struct argv_array args = ARGV_ARRAY_INIT; + const char *patch_path = ".git/stash_patch.patch"; + FILE *patch_file; - /* -* Apply currently only reads either from stdin or a file, thus -* apply_all_patches would have to be updated to optionally take a -* buffer. -*/ - cp.git_cmd = 1; - argv_array_pushl(, "apply", "--cached", NULL); - return pipe_command(, out->buf, out->len, NULL, 0, NULL, 0); + if (init_apply_state(, NULL)) + return -1; + + state.cached = cached; + state.apply_in_reverse = reverse; + state.check_index = check_index; + if (state.cached) + state.check_index = 1; + if (state.check_index) + state.unsafe_paths = 0; + + patch_file = fopen(patch_path, "w"); + strbuf_write(patch, patch_file); + fclose(patch_file); + + argv_array_push(, patch_path); + ret = apply_all_patches(, args.argc, args.argv, 0); + + remove_path(patch_path); + clear_apply_state(); + return ret; } static int reset_head(const char *prefix) @@ -418,7 +433,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, return -1; } - ret = apply_cached(); + ret = apply_patch_from_buf(, 1, 0, 0); strbuf_release(); if (ret) return -1; @@ -1341,7 +1356,6 @@ static int do_push_stash(int argc, const char **argv, const char *prefix, int i; struct child_process cp1 = CHILD_PROCESS_INIT; struct child_process cp2 = CHILD_PROCESS_INIT; - struct child_process cp3 = CHILD_PROCESS_INIT; struct strbuf out = STRBUF_INIT; cp1.git_cmd = 1; @@ -1365,11 +1379,9 @@ static int do_push_stash(int argc, const char **argv, const char *prefix, if (pipe_command(, NULL, 0, , 0, NULL, 0)) return -1; - cp3.git_cmd = 1; - argv_array_pushl(, "apply", "--index", "-R", -NULL); - if (pipe_command(, out.buf, out.len, NULL, 0, NULL, -0)) + discard_cache(); + read_cache(); + if (apply_patch_from_buf(, 0, 1, 1)) return -1; } else { struct child_process cp = CHILD_PROCESS_INIT; @@ -1405,12 +1417,7 @@ static int do_push_stash(int argc, const char **argv, const char *prefix, return -1; } } else { - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - argv_array_pushl(, "apply", "-R", NULL); - - if (pipe_command(, patch.buf, patch.len, NULL, 0, NULL, 0)) { + if (apply_patch_from_buf(, 0, 1, 0)) { if (!quiet)
Re: [PATCH 09/11] builtin rebase: start a new rebase only if none is in progress
On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki wrote: > > To run a new rebase, there needs to be a check to assure that no other > rebase is in progress. New rebase operation cannot start until an > ongoing rebase operation completes or is terminated. > > Signed-off-by: Pratik Karki > --- > builtin/rebase.c | 48 +++- > 1 file changed, 47 insertions(+), 1 deletion(-) > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index 8a7bf3d468..a261f552f1 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -87,6 +87,7 @@ struct rebase_options { > REBASE_VERBOSE = 1<<1, > REBASE_DIFFSTAT = 1<<2, > REBASE_FORCE = 1<<3, > + REBASE_INTERACTIVE_EXPLICIT = 1<<4, > } flags; > struct strbuf git_am_opt; > }; > @@ -392,10 +393,11 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > .git_am_opt = STRBUF_INIT, > }; > const char *branch_name; > - int ret, flags; > + int ret, flags, in_progress = 0; > int ok_to_skip_pre_rebase = 0; > struct strbuf msg = STRBUF_INIT; > struct strbuf revisions = STRBUF_INIT; > + struct strbuf buf = STRBUF_INIT; > struct object_id merge_base; > struct option builtin_rebase_options[] = { > OPT_STRING(0, "onto", _name, > @@ -447,6 +449,30 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > > git_config(rebase_config, ); > > + if (is_directory(apply_dir())) { > + options.type = REBASE_AM; > + options.state_dir = apply_dir(); > + } else if (is_directory(merge_dir())) { > + strbuf_reset(); > + strbuf_addf(, "%s/rewritten", merge_dir()); > + if (is_directory(buf.buf)) { > + options.type = REBASE_PRESERVE_MERGES; > + options.flags |= REBASE_INTERACTIVE_EXPLICIT; > + } else { > + strbuf_reset(); > + strbuf_addf(, "%s/interactive", merge_dir()); > + if(file_exists(buf.buf)) { > + options.type = REBASE_INTERACTIVE; > + options.flags |= REBASE_INTERACTIVE_EXPLICIT; > + } else > + options.type = REBASE_MERGE; > + } > + options.state_dir = merge_dir(); > + } > + > + if (options.type != REBASE_UNSPECIFIED) > + in_progress = 1; > + > argc = parse_options(argc, argv, prefix, > builtin_rebase_options, > builtin_rebase_usage, 0); > @@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) > usage_with_options(builtin_rebase_usage, >builtin_rebase_options); > > + /* Make sure no rebase is in progress */ The faithful conversion doesn't even stop at the comments. ;-) I shortly wondered if this is the best place for this comment, but let's just keep it here to have the 1:1 rewrite. > + if (in_progress) { [...] > + state_dir_base, cmd_live_rebase,buf.buf); In case a resend is needed, add a whitespace after the comma and buf.buf, please. So far I have not seen anything major that would warrant a resend. Thanks, Stefan
[GSoC][PATCH v7 04/26] stash: renamed test cases to be more descriptive
Renamed some test cases' labels to be more descriptive and under 80 characters per line. Signed-off-by: Paul-Sebastian Ungureanu --- t/t3903-stash.sh | 14 +++--- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index de6cab1fe..8d002a7f2 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -604,7 +604,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' ' test_cmp expected actual ' -test_expect_success 'stash drop - fail early if specified stash is not a stash reference' ' +test_expect_success 'drop: fail early if specified stash is not a stash ref' ' git stash clear && test_when_finished "git reset --hard HEAD && git stash clear" && git reset --hard && @@ -618,7 +618,7 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r git reset --hard HEAD ' -test_expect_success 'stash pop - fail early if specified stash is not a stash reference' ' +test_expect_success 'pop: fail early if specified stash is not a stash ref' ' git stash clear && test_when_finished "git reset --hard HEAD && git stash clear" && git reset --hard && @@ -682,7 +682,7 @@ test_expect_success 'invalid ref of the form "n", n >= N' ' git stash drop ' -test_expect_success 'stash branch should not drop the stash if the branch exists' ' +test_expect_success 'branch: should not drop the stash if the branch exists' ' git stash clear && echo foo >file && git add file && @@ -693,7 +693,7 @@ test_expect_success 'stash branch should not drop the stash if the branch exists git rev-parse stash@{0} -- ' -test_expect_success 'stash branch should not drop the stash if the apply fails' ' +test_expect_success 'branch: should not drop the stash if the apply fails' ' git stash clear && git reset HEAD~1 --hard && echo foo >file && @@ -707,7 +707,7 @@ test_expect_success 'stash branch should not drop the stash if the apply fails' git rev-parse stash@{0} -- ' -test_expect_success 'stash apply shows status same as git status (relative to current directory)' ' +test_expect_success 'apply: shows same status as git status (relative to ./)' ' git stash clear && echo 1 >subdir/subfile1 && echo 2 >subdir/subfile2 && @@ -1048,7 +1048,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' ' test_i18ncmp expect actual ' -test_expect_success 'stash push with pathspec shows no changes when there are none' ' +test_expect_success 'push: shows no changes when there are none' ' >foo && git add foo && git commit -m "tmp" && @@ -1058,7 +1058,7 @@ test_expect_success 'stash push with pathspec shows no changes when there are no test_i18ncmp expect actual ' -test_expect_success 'stash push with pathspec not in the repository errors out' ' +test_expect_success 'push: not in the repository errors out' ' >untracked && test_must_fail git stash push untracked && test_path_is_file untracked -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 12/26] stash: refactor `show_stash()` to use the diff API
Currently, `show_stash()` uses `cmd_diff()` to generate the output. After this commit, the output will be generated using the internal API. Before this commit, `git stash show --quiet` would act like `git diff` and error out if the stash is not empty. Now, the `--quiet` option does not error out given an empty stash. Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 73 + 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index 0c1efca6b..ec8c38c6f 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -10,6 +10,8 @@ #include "run-command.h" #include "dir.h" #include "rerere.h" +#include "revision.h" +#include "log-tree.h" static const char * const git_stash_helper_usage[] = { N_("git stash--helper list []"), @@ -662,56 +664,71 @@ static int git_stash_config(const char *var, const char *value, void *cb) static int show_stash(int argc, const char **argv, const char *prefix) { - int i, ret = 0; - struct child_process cp = CHILD_PROCESS_INIT; - struct argv_array args_refs = ARGV_ARRAY_INIT; + int i; + int flags = 0; struct stash_info info; + struct rev_info rev; + struct argv_array stash_args = ARGV_ARRAY_INIT; struct option options[] = { OPT_END() }; - argc = parse_options(argc, argv, prefix, options, -git_stash_helper_show_usage, -PARSE_OPT_KEEP_UNKNOWN); + init_diff_ui_defaults(); + git_config(git_diff_ui_config, NULL); - cp.git_cmd = 1; - argv_array_push(, "diff"); + init_revisions(, prefix); - /* Push arguments which are not options into args_refs. */ - for (i = 0; i < argc; ++i) { - if (argv[i][0] == '-') - argv_array_push(, argv[i]); + /* Push arguments which are not options into stash_args. */ + for (i = 1; i < argc; ++i) { + if (argv[i][0] != '-') + argv_array_push(_args, argv[i]); else - argv_array_push(_refs, argv[i]); - } - - if (get_stash_info(, args_refs.argc, args_refs.argv)) { - child_process_clear(); - argv_array_clear(_refs); - return -1; + flags++; } /* * The config settings are applied only if there are not passed * any flags. */ - if (cp.args.argc == 1) { + if (!flags) { git_config(git_stash_config, NULL); if (show_stat) - argv_array_push(, "--stat"); + rev.diffopt.output_format |= DIFF_FORMAT_DIFFSTAT; + if (show_patch) { + rev.diffopt.output_format = ~DIFF_FORMAT_NO_OUTPUT; + rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + } + } - if (show_patch) - argv_array_push(, "-p"); + if (get_stash_info(, stash_args.argc, stash_args.argv)) { + argv_array_clear(_args); + return -1; } - argv_array_pushl(, oid_to_hex(_commit), -oid_to_hex(_commit), NULL); + argc = setup_revisions(argc, argv, , NULL); + if (!rev.diffopt.output_format) + rev.diffopt.output_format = DIFF_FORMAT_PATCH; + diff_setup_done(); + rev.diffopt.flags.recursive = 1; + setup_diff_pager(); - ret = run_command(); + /* +* We can return early if there was any option not recognised by +* `diff_opt_parse()`, besides the word `stash`. +*/ + if (argc > 1) { + free_stash_info(); + argv_array_clear(_args); + usage_with_options(git_stash_helper_show_usage, options); + } + + /* Do the diff thing. */ + diff_tree_oid(_commit, _commit, "", ); + log_tree_diff_flush(); free_stash_info(); - argv_array_clear(_refs); - return ret; + argv_array_clear(_args); + return 0; } int cmd_stash__helper(int argc, const char **argv, const char *prefix) -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 18/26] stash: convert push to builtin
Add stash push to the helper. --- builtin/stash--helper.c | 209 git-stash.sh| 6 +- 2 files changed, 213 insertions(+), 2 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index f905d3908..c26cad3d5 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -23,6 +23,9 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper clear"), N_("git stash--helper store [-m|--message ] [-q|--quiet] "), N_("git stash--helper create []"), + N_("git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" + " [-u|--include-untracked] [-a|--all] [-m|--message ]\n" + " [--] [...]]"), NULL }; @@ -71,6 +74,13 @@ static const char * const git_stash_helper_create_usage[] = { NULL }; +static const char * const git_stash_helper_push_usage[] = { + N_("git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" + " [-u|--include-untracked] [-a|--all] [-m|--message ]\n" + " [--] [...]]"), + NULL +}; + static const char *ref_stash = "refs/stash"; static int quiet; static struct strbuf stash_index_path = STRBUF_INIT; @@ -1210,6 +1220,203 @@ static int create_stash(int argc, const char **argv, const char *prefix) return ret < 0; } +static int do_push_stash(int argc, const char **argv, const char *prefix, +int keep_index, int patch_mode, int include_untracked, +int quiet, const char *stash_msg) +{ + int ret = 0; + struct pathspec ps; + struct stash_info info; + if (patch_mode && keep_index == -1) + keep_index = 1; + + if (patch_mode && include_untracked) { + fprintf_ln(stderr, "Can't use --patch and --include-untracked or --all at the same time"); + return -1; + } + + parse_pathspec(, 0, PATHSPEC_PREFER_FULL, prefix, argv); + + if (read_cache() < 0) + die(_("index file corrupt")); + + if (!include_untracked && ps.nr) { + int i; + char *ps_matched = xcalloc(ps.nr, 1); + + for (i = 0; i < active_nr; ++i) { + const struct cache_entry *ce = active_cache[i]; + if (!ce_path_match(ce, , ps_matched)) + continue; + } + + if (report_path_error(ps_matched, , prefix)) { + fprintf_ln(stderr, "Did you forget to 'git add'?"); + return -1; + } + } + + read_cache_preload(NULL); + if (refresh_cache(REFRESH_QUIET)) + return -1; + + if (!check_changes(argv, include_untracked, prefix)) { + fprintf_ln(stdout, "No local changes to save"); + return 0; + } + + if (!reflog_exists(ref_stash) && do_clear_stash()) { + fprintf_ln(stderr, "Cannot initialize stash"); + return -1; + } + + if ((ret = do_create_stash(argc, argv, prefix, _msg, + include_untracked, patch_mode, ))) + return ret; + + if (do_store_stash(oid_to_hex(_commit), stash_msg, 1)) { + fprintf(stderr, "Cannot save the current status"); + return -1; + } + + fprintf(stdout, "Saved working directory and index state %s", stash_msg); + + if (!patch_mode) { + if (include_untracked && ps.nr == 0) { + struct child_process cp = CHILD_PROCESS_INIT; + + cp.git_cmd = 1; + argv_array_pushl(, "clean", "--force", +"--quiet", "-d", NULL); + if (include_untracked == 2) + argv_array_push(, "-x"); + if (run_command()) + return -1; + } + if (argc != 0) { + int i; + struct child_process cp1 = CHILD_PROCESS_INIT; + struct child_process cp2 = CHILD_PROCESS_INIT; + struct child_process cp3 = CHILD_PROCESS_INIT; + struct strbuf out = STRBUF_INIT; + + cp1.git_cmd = 1; + argv_array_push(, "add"); + if (!include_untracked) + argv_array_push(, "-u"); + if (include_untracked == 2) + argv_array_push(, "--force"); + argv_array_push(, "--"); + for (i = 0; i < ps.nr; ++i) + argv_array_push(, ps.items[i].match); + if (run_command()) + return
[GSoC][PATCH v7 06/26] stash: convert drop and clear to builtin
From: Joel Teichroeb Add the drop and clear commands to the builtin helper. These two are each simple, but are being added together as they are quite related. We have to unfortunately keep the drop and clear functions in the shell script as functions are called with parameters internally that are not valid when the commands are called externally. Once pop is converted they can both be removed. Signed-off-by: Joel Teichroeb Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 115 git-stash.sh| 4 +- 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index ef6a9d30d..ae719b7fc 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -12,7 +12,14 @@ #include "rerere.h" static const char * const git_stash_helper_usage[] = { + N_("git stash--helper drop [-q|--quiet] []"), N_("git stash--helper apply [--index] [-q|--quiet] []"), + N_("git stash--helper clear"), + NULL +}; + +static const char * const git_stash_helper_drop_usage[] = { + N_("git stash--helper drop [-q|--quiet] []"), NULL }; @@ -21,6 +28,11 @@ static const char * const git_stash_helper_apply_usage[] = { NULL }; +static const char * const git_stash_helper_clear_usage[] = { + N_("git stash--helper clear"), + NULL +}; + static const char *ref_stash = "refs/stash"; static int quiet; static struct strbuf stash_index_path = STRBUF_INIT; @@ -140,6 +152,31 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) return !(ret == 0 || ret == 1); } +static int do_clear_stash(void) +{ + struct object_id obj; + if (get_oid(ref_stash, )) + return 0; + + return delete_ref(NULL, ref_stash, , 0); +} + +static int clear_stash(int argc, const char **argv, const char *prefix) +{ + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_clear_usage, +PARSE_OPT_STOP_AT_NON_OPTION); + + if (argc != 0) + return error(_("git stash--helper clear with parameters is unimplemented")); + + return do_clear_stash(); +} + static int reset_tree(struct object_id *i_tree, int update, int reset) { struct unpack_trees_options opts; @@ -424,6 +461,80 @@ static int apply_stash(int argc, const char **argv, const char *prefix) return ret; } +static int do_drop_stash(const char *prefix, struct stash_info *info) +{ + struct child_process cp_reflog = CHILD_PROCESS_INIT; + struct child_process cp = CHILD_PROCESS_INIT; + int ret; + + /* +* reflog does not provide a simple function for deleting refs. One will +* need to be added to avoid implementing too much reflog code here +*/ + + cp_reflog.git_cmd = 1; + argv_array_pushl(_reflog.args, "reflog", "delete", "--updateref", +"--rewrite", NULL); + argv_array_push(_reflog.args, info->revision.buf); + ret = run_command(_reflog); + if (!ret) { + if (!quiet) + printf(_("Dropped %s (%s)\n"), info->revision.buf, + oid_to_hex(>w_commit)); + } else { + return error(_("%s: Could not drop stash entry"), +info->revision.buf); + } + + /* +* This could easily be replaced by get_oid, but currently it will throw +* a fatal error when a reflog is empty, which we can not recover from. +*/ + cp.git_cmd = 1; + /* Even though --quiet is specified, rev-parse still outputs the hash */ + cp.no_stdout = 1; + argv_array_pushl(, "rev-parse", "--verify", "--quiet", NULL); + argv_array_pushf(, "%s@{0}", ref_stash); + ret = run_command(); + + /* do_clear_stash if we just dropped the last stash entry */ + if (ret) + do_clear_stash(); + + return 0; +} + +static void assert_stash_ref(struct stash_info *info) +{ + if (!info->is_stash_ref) { + free_stash_info(info); + error(_("'%s' is not a stash reference"), info->revision.buf); + exit(128); + } +} + +static int drop_stash(int argc, const char **argv, const char *prefix) +{ + struct stash_info info; + int ret; + struct option options[] = { + OPT__QUIET(, N_("be quiet, only report errors")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_drop_usage, 0); + + if (get_stash_info(, argc, argv)) + return -1; + + assert_stash_ref(); + + ret = do_drop_stash(prefix, ); + free_stash_info(); + return ret; +} + int
[GSoC][PATCH v7 01/26] sha1-name.c: added 'get_oidf', which acts like 'get_oid'
Compared to 'get_oid', 'get_oidf' has as parameters a printf format string and the additional arguments. This will help simplify the code in subsequent commits. Original-idea-by: Johannes Schindelin Signed-off-by: Paul-Sebastian Ungureanu --- cache.h | 1 + sha1-name.c | 19 +++ 2 files changed, 20 insertions(+) diff --git a/cache.h b/cache.h index 8dc7134f0..4ec09b336 100644 --- a/cache.h +++ b/cache.h @@ -1321,6 +1321,7 @@ struct object_context { GET_OID_BLOB) extern int get_oid(const char *str, struct object_id *oid); +extern int get_oidf(struct object_id *oid, const char *fmt, ...); extern int get_oid_commit(const char *str, struct object_id *oid); extern int get_oid_committish(const char *str, struct object_id *oid); extern int get_oid_tree(const char *str, struct object_id *oid); diff --git a/sha1-name.c b/sha1-name.c index c9cc1318b..261b960bb 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -1471,6 +1471,25 @@ int get_oid(const char *name, struct object_id *oid) return get_oid_with_context(name, 0, oid, ); } +/* + * This returns a non-zero value if the string (built using printf + * format and the given arguments) is not a valid object. + */ +int get_oidf(struct object_id *oid, const char *fmt, ...) +{ + va_list ap; + int ret; + struct strbuf sb = STRBUF_INIT; + + va_start(ap, fmt); + strbuf_vaddf(, fmt, ap); + va_end(ap); + + ret = get_oid(sb.buf, oid); + strbuf_release(); + + return ret; +} /* * Many callers know that the user meant to name a commit-ish by -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 13/26] stash: update `git stash show` documentation
Add in documentation about the change of behavior regarding the `--quiet` option, which was introduced in the last commit. (the `--quiet` option does not exit anymore with erorr if it is given an empty stash as argument) Signed-off-by: Paul-Sebastian Ungureanu --- Documentation/git-stash.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index e31ea7d30..d60ebdb96 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -117,6 +117,9 @@ show [] []:: You can use stash.showStat and/or stash.showPatch config variables to change the default behavior. + It accepts any option known to `git diff`, but acts different on + `--quiet` option and exit with zero regardless of differences. + pop [--index] [-q|--quiet] []:: Remove a single stashed state from the stash list and apply it -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 23/26] stash: convert `stash--helper.c` into `stash.c`
The old shell script `git-stash.sh` was removed and replaced entirely by `builtin/stash.c`. In order to do that, `create` and `push` were adapted to work without `stash.sh`. For example, before this commit, `git stash create` called `git stash--helper create --message "$*"`. If it called `git stash--helper create "$@"`, then some of these changes wouldn't have been necessary. This commit also removes the word `helper` since now stash is called directly and not by a shell script. --- .gitignore | 1 - Makefile | 3 +- builtin.h| 2 +- builtin/{stash--helper.c => stash.c} | 132 --- git-stash.sh | 153 --- git.c| 2 +- 6 files changed, 74 insertions(+), 219 deletions(-) rename builtin/{stash--helper.c => stash.c} (91%) delete mode 100755 git-stash.sh diff --git a/.gitignore b/.gitignore index 3abbba81a..3284a1e9b 100644 --- a/.gitignore +++ b/.gitignore @@ -156,7 +156,6 @@ /git-show-ref /git-stage /git-stash -/git-stash--helper /git-status /git-stripspace /git-submodule diff --git a/Makefile b/Makefile index af82bc7ee..c41e31ad8 100644 --- a/Makefile +++ b/Makefile @@ -612,7 +612,6 @@ SCRIPT_SH += git-quiltimport.sh SCRIPT_SH += git-rebase.sh SCRIPT_SH += git-remote-testgit.sh SCRIPT_SH += git-request-pull.sh -SCRIPT_SH += git-stash.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh @@ -1083,7 +1082,7 @@ BUILTIN_OBJS += builtin/shortlog.o BUILTIN_OBJS += builtin/show-branch.o BUILTIN_OBJS += builtin/show-index.o BUILTIN_OBJS += builtin/show-ref.o -BUILTIN_OBJS += builtin/stash--helper.o +BUILTIN_OBJS += builtin/stash.o BUILTIN_OBJS += builtin/stripspace.o BUILTIN_OBJS += builtin/submodule--helper.o BUILTIN_OBJS += builtin/symbolic-ref.o diff --git a/builtin.h b/builtin.h index 55ceb1574..e8102d825 100644 --- a/builtin.h +++ b/builtin.h @@ -222,7 +222,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix); extern int cmd_show_index(int argc, const char **argv, const char *prefix); extern int cmd_status(int argc, const char **argv, const char *prefix); -extern int cmd_stash__helper(int argc, const char **argv, const char *prefix); +extern int cmd_stash(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix); extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); diff --git a/builtin/stash--helper.c b/builtin/stash.c similarity index 91% rename from builtin/stash--helper.c rename to builtin/stash.c index f54a476e3..0ef88408a 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash.c @@ -14,77 +14,77 @@ #include "log-tree.h" #include "diffcore.h" -static const char * const git_stash_helper_usage[] = { - N_("git stash--helper list []"), - N_("git stash--helper show [] []"), - N_("git stash--helper drop [-q|--quiet] []"), - N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] []"), - N_("git stash--helper branch []"), - N_("git stash--helper clear"), - N_("git stash--helper store [-m|--message ] [-q|--quiet] "), - N_("git stash--helper create []"), - N_("git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" +static const char * const git_stash_usage[] = { + N_("git stash list []"), + N_("git stash show [] []"), + N_("git stash drop [-q|--quiet] []"), + N_("git stash ( pop | apply ) [--index] [-q|--quiet] []"), + N_("git stash branch []"), + N_("git stash clear"), + N_("git stash store [-m|--message ] [-q|--quiet] "), + N_("git stash create []"), + N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" " [-u|--include-untracked] [-a|--all] [-m|--message ]\n" " [--] [...]]"), - N_("git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" + N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" " [-u|--include-untracked] [-a|--all] []"), NULL }; -static const char * const git_stash_helper_list_usage[] = { - N_("git stash--helper list []"), +static const char * const git_stash_list_usage[] = { + N_("git stash list []"), NULL }; -static const char * const git_stash_helper_show_usage[] = { - N_("git stash--helper show [] []"), +static const char * const git_stash_show_usage[] = { + N_("git stash show [] []"), NULL }; -static const char * const git_stash_helper_drop_usage[] = { - N_("git stash--helper drop [-q|--quiet] []"), +static const char * const git_stash_drop_usage[] = { + N_("git
[GSoC][PATCH v7 00/26] Convert "git stash" to C builtin
Hello, Here is the whole `git stash` C version. Some of the previous patches were already reviewed (up to and including "stash: convert store to builtin"), but there are some which were not (starting with "stash: convert create to builtin"). In order to see the difference between the shell version and the C version, I ran `time` on: * git test suite (t3903-stash.sh, t3904-stash-patch.sh, t3905-stash-include-untracked.sh and t3906-stash-submodule.sh) t3903-stash.sh: ** SHELL: 12,69s user 9,95s system 109% cpu 20,730 total ** C: 2,67s user 2,84s system 105% cpu 5,206 total t3904-stash-patch.sh: ** SHELL: 1,43s user 0,94s system 106% cpu 2,242 total ** C: 1,01s user 0,58s system 104% cpu 1,530 total t3905-stash-include-untracked.sh ** SHELL: 2,22s user 1,73s system 110% cpu 3,569 total ** C: 0,59s user 0,57s system 106% cpu 1,085 total t3906-stash-submodule.sh ** SHELL: 2,89s user 2,99s system 106% cpu 5,527 total ** C: 2,21s user 2,61s system 105% cpu 4,568 total TOTAL: ** SHELL: 19.23s user 15.61s system ** C: 6.48s user 6.60s system * a git repository with 4000 files: 1000 not changed, 1000 staged files, 1000 unstaged files, 1000 untracked. In this case I ran some of the most used commands: git stash push: ** SHELL: 0,12s user 0,21s system 101% cpu 0,329 total ** C: 0,06s user 0,13s system 105% cpu 0,185 total git stash push -u: ** SHELL: 0,18s user 0,27s system 108% cpu 0,401 total ** C: 0,09s user 0,19s system 103% cpu 0,267 total git stash pop: ** SHELL: 0,16s user 0,26s system 103% cpu 0,399 total ** C: 0,13s user 0,19s system 102% cpu 0,308 total Best regards, Paul Ungureanu Joel Teichroeb (5): stash: improve option parsing test coverage stash: convert apply to builtin stash: convert drop and clear to builtin stash: convert branch to builtin stash: convert pop to builtin Paul-Sebastian Ungureanu (21): sha1-name.c: added 'get_oidf', which acts like 'get_oid' stash: update test cases conform to coding guidelines stash: renamed test cases to be more descriptive stash: implement the "list" command in the builtin stash: convert show to builtin stash: change `git stash show` usage text and documentation stash: refactor `show_stash()` to use the diff API stash: update `git stash show` documentation stash: convert store to builtin stash: convert create to builtin stash: replace spawning a "read-tree" process stash: avoid spawning a "diff-index" process stash: convert push to builtin stash: make push to be quiet stash: add tests for `git stash push -q` stash: replace spawning `git ls-files` child process stash: convert save to builtin stash: convert `stash--helper.c` into `stash.c` stash: optimize `get_untracked_files()` and `check_changes()` stash: replace all `write-tree` child processes with API calls stash: replace all "git apply" child processes with API calls Documentation/git-stash.txt |7 +- Makefile|2 +- builtin.h |1 + builtin/stash.c | 1562 +++ cache.h |1 + git-stash.sh| 752 - git.c |1 + sha1-name.c | 19 + t/t3903-stash.sh| 190 +++-- 9 files changed, 1714 insertions(+), 821 deletions(-) create mode 100644 builtin/stash.c delete mode 100755 git-stash.sh -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 05/26] stash: convert apply to builtin
From: Joel Teichroeb Add a builtin helper for performing stash commands. Converting all at once proved hard to review, so starting with just apply lets conversion get started without the other commands being finished. The helper is being implemented as a drop in replacement for stash so that when it is complete it can simply be renamed and the shell script deleted. Delete the contents of the apply_stash shell function and replace it with a call to stash--helper apply until pop is also converted. Signed-off-by: Joel Teichroeb Signed-off-by: Paul-Sebastian Ungureanu --- .gitignore | 1 + Makefile| 1 + builtin.h | 1 + builtin/stash--helper.c | 452 git-stash.sh| 78 +-- git.c | 1 + 6 files changed, 463 insertions(+), 71 deletions(-) create mode 100644 builtin/stash--helper.c diff --git a/.gitignore b/.gitignore index 3284a1e9b..3abbba81a 100644 --- a/.gitignore +++ b/.gitignore @@ -156,6 +156,7 @@ /git-show-ref /git-stage /git-stash +/git-stash--helper /git-status /git-stripspace /git-submodule diff --git a/Makefile b/Makefile index bc4fc8eea..af82bc7ee 100644 --- a/Makefile +++ b/Makefile @@ -1083,6 +1083,7 @@ BUILTIN_OBJS += builtin/shortlog.o BUILTIN_OBJS += builtin/show-branch.o BUILTIN_OBJS += builtin/show-index.o BUILTIN_OBJS += builtin/show-ref.o +BUILTIN_OBJS += builtin/stash--helper.o BUILTIN_OBJS += builtin/stripspace.o BUILTIN_OBJS += builtin/submodule--helper.o BUILTIN_OBJS += builtin/symbolic-ref.o diff --git a/builtin.h b/builtin.h index 0362f1ce2..55ceb1574 100644 --- a/builtin.h +++ b/builtin.h @@ -222,6 +222,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_show_branch(int argc, const char **argv, const char *prefix); extern int cmd_show_index(int argc, const char **argv, const char *prefix); extern int cmd_status(int argc, const char **argv, const char *prefix); +extern int cmd_stash__helper(int argc, const char **argv, const char *prefix); extern int cmd_stripspace(int argc, const char **argv, const char *prefix); extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix); extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c new file mode 100644 index 0..ef6a9d30d --- /dev/null +++ b/builtin/stash--helper.c @@ -0,0 +1,452 @@ +#include "builtin.h" +#include "config.h" +#include "parse-options.h" +#include "refs.h" +#include "lockfile.h" +#include "cache-tree.h" +#include "unpack-trees.h" +#include "merge-recursive.h" +#include "argv-array.h" +#include "run-command.h" +#include "dir.h" +#include "rerere.h" + +static const char * const git_stash_helper_usage[] = { + N_("git stash--helper apply [--index] [-q|--quiet] []"), + NULL +}; + +static const char * const git_stash_helper_apply_usage[] = { + N_("git stash--helper apply [--index] [-q|--quiet] []"), + NULL +}; + +static const char *ref_stash = "refs/stash"; +static int quiet; +static struct strbuf stash_index_path = STRBUF_INIT; + +/* + * w_commit is set to the commit containing the working tree + * b_commit is set to the base commit + * i_commit is set to the commit containing the index tree + * u_commit is set to the commit containing the untracked files tree + * w_tree is set to the working tree + * b_tree is set to the base tree + * i_tree is set to the index tree + * u_tree is set to the untracked files tree + */ + +struct stash_info { + struct object_id w_commit; + struct object_id b_commit; + struct object_id i_commit; + struct object_id u_commit; + struct object_id w_tree; + struct object_id b_tree; + struct object_id i_tree; + struct object_id u_tree; + struct strbuf revision; + int is_stash_ref; + int has_u; +}; + +static void free_stash_info(struct stash_info *info) +{ + strbuf_release(>revision); +} + +static void assert_stash_like(struct stash_info *info, const char * revision) +{ + if (get_oidf(>b_commit, "%s^1", revision) || + get_oidf(>w_tree, "%s:", revision) || + get_oidf(>b_tree, "%s^1:", revision) || + get_oidf(>i_tree, "%s^2:", revision)) { + free_stash_info(info); + error(_("'%s' is not a stash-like commit"), revision); + exit(128); + } +} + +static int get_stash_info(struct stash_info *info, int argc, const char **argv) +{ + struct strbuf symbolic = STRBUF_INIT; + int ret; + const char *revision; + const char *commit = NULL; + char *end_of_rev; + char *expanded_ref; + struct object_id dummy; + + if (argc > 1) { + int i; + struct strbuf refs_msg = STRBUF_INIT; + for (i = 0; i < argc; ++i) + strbuf_addf(_msg, " '%s'",
[GSoC][PATCH v7 09/26] stash: implement the "list" command in the builtin
Add stash list to the helper and delete the list_stash function from the shell script. Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 31 +++ git-stash.sh| 7 +-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index d6bd468e0..daa4d0034 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -12,6 +12,7 @@ #include "rerere.h" static const char * const git_stash_helper_usage[] = { + N_("git stash--helper list []"), N_("git stash--helper drop [-q|--quiet] []"), N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] []"), N_("git stash--helper branch []"), @@ -19,6 +20,11 @@ static const char * const git_stash_helper_usage[] = { NULL }; +static const char * const git_stash_helper_list_usage[] = { + N_("git stash--helper list []"), + NULL +}; + static const char * const git_stash_helper_drop_usage[] = { N_("git stash--helper drop [-q|--quiet] []"), NULL @@ -609,6 +615,29 @@ static int branch_stash(int argc, const char **argv, const char *prefix) return ret; } +static int list_stash(int argc, const char **argv, const char *prefix) +{ + struct child_process cp = CHILD_PROCESS_INIT; + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_list_usage, +PARSE_OPT_KEEP_UNKNOWN); + + if (!ref_exists(ref_stash)) + return 0; + + cp.git_cmd = 1; + argv_array_pushl(, "log", "--format=%gd: %gs", "-g", +"--first-parent", "-m", NULL); + argv_array_pushv(, argv); + argv_array_push(, ref_stash); + argv_array_push(, "--"); + return run_command(); +} + int cmd_stash__helper(int argc, const char **argv, const char *prefix) { pid_t pid = getpid(); @@ -639,6 +668,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) return !!pop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "branch")) return !!branch_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "list")) + return !!list_stash(argc, argv, prefix); usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_helper_usage, options); diff --git a/git-stash.sh b/git-stash.sh index 8f2640fe9..6052441aa 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -382,11 +382,6 @@ have_stash () { git rev-parse --verify --quiet $ref_stash >/dev/null } -list_stash () { - have_stash || return 0 - git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash -- -} - show_stash () { ALLOW_UNKNOWN_FLAGS=t assert_stash_like "$@" @@ -574,7 +569,7 @@ test -n "$seen_non_option" || set "push" "$@" case "$1" in list) shift - list_stash "$@" + git stash--helper list "$@" ;; show) shift -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 10/26] stash: convert show to builtin
Add stash show to the helper and delete the show_stash, have_stash, assert_stash_like, is_stash_like and parse_flags_and_rev functions from the shell script now that they are no longer needed. Before this commit, `git stash show` would ignore `--index` and `--quiet` options. Now, `git stash show` errors out on `--index` and does not display any message on `--quiet`, but errors out if the stash is not empty. Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 78 git-stash.sh| 132 +--- 2 files changed, 79 insertions(+), 131 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index daa4d0034..e764cd33e 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -13,6 +13,7 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper list []"), + N_("git stash--helper show []"), N_("git stash--helper drop [-q|--quiet] []"), N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] []"), N_("git stash--helper branch []"), @@ -25,6 +26,11 @@ static const char * const git_stash_helper_list_usage[] = { NULL }; +static const char * const git_stash_helper_show_usage[] = { + N_("git stash--helper show []"), + NULL +}; + static const char * const git_stash_helper_drop_usage[] = { N_("git stash--helper drop [-q|--quiet] []"), NULL @@ -638,6 +644,76 @@ static int list_stash(int argc, const char **argv, const char *prefix) return run_command(); } +static int show_stat = 1; +static int show_patch; + +static int git_stash_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "stash.showStat")) { + show_stat = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "stash.showPatch")) { + show_patch = git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, cb); +} + +static int show_stash(int argc, const char **argv, const char *prefix) +{ + int i, ret = 0; + struct child_process cp = CHILD_PROCESS_INIT; + struct argv_array args_refs = ARGV_ARRAY_INIT; + struct stash_info info; + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_show_usage, +PARSE_OPT_KEEP_UNKNOWN); + + cp.git_cmd = 1; + argv_array_push(, "diff"); + + /* Push arguments which are not options into args_refs. */ + for (i = 0; i < argc; ++i) { + if (argv[i][0] == '-') + argv_array_push(, argv[i]); + else + argv_array_push(_refs, argv[i]); + } + + if (get_stash_info(, args_refs.argc, args_refs.argv)) { + child_process_clear(); + argv_array_clear(_refs); + return -1; + } + + /* +* The config settings are applied only if there are not passed +* any flags. +*/ + if (cp.args.argc == 1) { + git_config(git_stash_config, NULL); + if (show_stat) + argv_array_push(, "--stat"); + + if (show_patch) + argv_array_push(, "-p"); + } + + argv_array_pushl(, oid_to_hex(_commit), +oid_to_hex(_commit), NULL); + + ret = run_command(); + + free_stash_info(); + argv_array_clear(_refs); + return ret; +} + int cmd_stash__helper(int argc, const char **argv, const char *prefix) { pid_t pid = getpid(); @@ -670,6 +746,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) return !!branch_stash(argc, argv, prefix); else if (!strcmp(argv[0], "list")) return !!list_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "show")) + return !!show_stash(argc, argv, prefix); usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_helper_usage, options); diff --git a/git-stash.sh b/git-stash.sh index 6052441aa..0d05cbc1e 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -378,35 +378,6 @@ save_stash () { fi } -have_stash () { - git rev-parse --verify --quiet $ref_stash >/dev/null -} - -show_stash () { - ALLOW_UNKNOWN_FLAGS=t - assert_stash_like "$@" - - if test -z "$FLAGS" - then - if test "$(git config --bool stash.showStat || echo true)" = "true" - then - FLAGS=--stat - fi - - if test "$(git config --bool stash.showPatch || echo false)" = "true" - then - FLAGS=${FLAGS}${FLAGS:+ }-p - fi - -
[GSoC][PATCH v7 08/26] stash: convert pop to builtin
From: Joel Teichroeb Add stash pop to the helper and delete the pop_stash, drop_stash, assert_stash_ref functions from the shell script now that they are no longer needed. Signed-off-by: Joel Teichroeb Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 36 ++- git-stash.sh| 47 ++--- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index 1e4d07295..d6bd468e0 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -13,7 +13,7 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper drop [-q|--quiet] []"), - N_("git stash--helper apply [--index] [-q|--quiet] []"), + N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] []"), N_("git stash--helper branch []"), N_("git stash--helper clear"), NULL @@ -24,6 +24,11 @@ static const char * const git_stash_helper_drop_usage[] = { NULL }; +static const char * const git_stash_helper_pop_usage[] = { + N_("git stash--helper pop [--index] [-q|--quiet] []"), + NULL +}; + static const char * const git_stash_helper_apply_usage[] = { N_("git stash--helper apply [--index] [-q|--quiet] []"), NULL @@ -541,6 +546,33 @@ static int drop_stash(int argc, const char **argv, const char *prefix) return ret; } +static int pop_stash(int argc, const char **argv, const char *prefix) +{ + int index = 0, ret; + struct stash_info info; + struct option options[] = { + OPT__QUIET(, N_("be quiet, only report errors")), + OPT_BOOL(0, "index", , + N_("attempt to recreate the index")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_pop_usage, 0); + + if (get_stash_info(, argc, argv)) + return -1; + + assert_stash_ref(); + if ((ret = do_apply_stash(prefix, , index))) + printf_ln(_("The stash entry is kept in case you need it again.")); + else + ret = do_drop_stash(prefix, ); + + free_stash_info(); + return ret; +} + static int branch_stash(int argc, const char **argv, const char *prefix) { const char *branch = NULL; @@ -603,6 +635,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) return !!clear_stash(argc, argv, prefix); else if (!strcmp(argv[0], "drop")) return !!drop_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "pop")) + return !!pop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "branch")) return !!branch_stash(argc, argv, prefix); diff --git a/git-stash.sh b/git-stash.sh index 29d9f4425..8f2640fe9 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -554,50 +554,6 @@ assert_stash_like() { } } -is_stash_ref() { - is_stash_like "$@" && test -n "$IS_STASH_REF" -} - -assert_stash_ref() { - is_stash_ref "$@" || { - args="$*" - die "$(eval_gettext "'\$args' is not a stash reference")" - } -} - -apply_stash () { - cd "$START_DIR" - git stash--helper apply "$@" - res=$? - cd_to_toplevel - return $res -} - -pop_stash() { - assert_stash_ref "$@" - - if apply_stash "$@" - then - drop_stash "$@" - else - status=$? - say "$(gettext "The stash entry is kept in case you need it again.")" - exit $status - fi -} - -drop_stash () { - assert_stash_ref "$@" - - git reflog delete --updateref --rewrite "${REV}" && - say "$(eval_gettext "Dropped \${REV} (\$s)")" || - die "$(eval_gettext "\${REV}: Could not drop stash entry")" - - # clear_stash if we just dropped the last stash entry - git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null || - clear_stash -} - test "$1" = "-p" && set "push" "$@" PARSE_CACHE='--not-parsed' @@ -655,7 +611,8 @@ drop) ;; pop) shift - pop_stash "$@" + cd "$START_DIR" + git stash--helper pop "$@" ;; branch) shift -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 24/26] stash: optimize `get_untracked_files()` and `check_changes()`
This commits introduces a optimization by avoiding calling the same functions again. For example, `git stash push -u` would call at some points the following functions: * `check_changes()` * `do_create_stash()`, which calls: `check_changes()` and `get_untracked_files()` Note that `check_changes()` also calls `get_untracked_files()`. So, `check_changes()` is called 2 times and `get_untracked_files()` 3 times. By checking at the beginning of the function if we already performed a check, we can avoid making useless calls. --- builtin/stash.c | 50 +++-- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index 0ef88408a..4d5c0d16e 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -819,13 +819,23 @@ static int store_stash(int argc, const char **argv, const char *prefix) } /* - * `out` will be filled with the names of untracked files. The return value is: + * `has_untracked_files` is: + * -2 if `get_untracked_files()` hasn't been called + * -1 if there were errors + * 0 if there are no untracked files + * 1 if there are untracked files + * + * `untracked_files` will be filled with the names of untracked files. + * The return value is: * * = 0 if there are not any untracked files * > 0 if there are untracked files */ +static struct strbuf untracked_files = STRBUF_INIT; +static int has_untracked_files = -2; + static int get_untracked_files(const char **argv, const char *prefix, - int include_untracked, struct strbuf *out) + int include_untracked) { int max_len; int i; @@ -833,6 +843,9 @@ static int get_untracked_files(const char **argv, const char *prefix, struct dir_struct dir; struct pathspec pathspec; + if (has_untracked_files != -2) + return has_untracked_files; + memset(, 0, sizeof(dir)); if (include_untracked != 2) setup_standard_excludes(); @@ -849,7 +862,7 @@ static int get_untracked_files(const char **argv, const char *prefix, free(ent); continue; } - strbuf_addf(out, "%s\n", ent->name); + strbuf_addf(_files, "%s\n", ent->name); free(ent); } @@ -857,16 +870,25 @@ static int get_untracked_files(const char **argv, const char *prefix, free(dir.ignored); clear_directory(); free(seen); - return out->len; + has_untracked_files = untracked_files.len; + return untracked_files.len; } /* + * `changes` is: + * -2 if `check_changes()` hasn't been called + * -1 if there were any errors + * 0 if there are no changes + * 1 if there are changes + * * The return value of `check_changes()` can be: * * < 0 if there was an error * = 0 if there are no changes. * > 0 if there are changes. */ +static int changes = -2; + static int check_changes(const char **argv, int include_untracked, const char *prefix) { @@ -874,9 +896,11 @@ static int check_changes(const char **argv, int include_untracked, int ret = 0; struct rev_info rev; struct object_id dummy; - struct strbuf out = STRBUF_INIT; struct argv_array args = ARGV_ARRAY_INIT; + if (changes != -2) + return changes; + init_revisions(, prefix); parse_pathspec(_data, 0, PATHSPEC_PREFER_FULL, prefix, argv); @@ -912,17 +936,16 @@ static int check_changes(const char **argv, int include_untracked, } if (include_untracked && get_untracked_files(argv, prefix, -include_untracked, )) +include_untracked)) ret = 1; done: - strbuf_release(); + changes = ret; argv_array_clear(); return ret; } -static int save_untracked_files(struct stash_info *info, struct strbuf *msg, - struct strbuf *out) +static int save_untracked_files(struct stash_info *info, struct strbuf *msg) { int ret = 0; struct strbuf untracked_msg = STRBUF_INIT; @@ -937,7 +960,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg, stash_index_path.buf); strbuf_addf(_msg, "untracked files on %s\n", msg->buf); - if (pipe_command(, out->buf, out->len, NULL, 0, NULL, 0)) { + if (pipe_command(, untracked_files.buf, untracked_files.len, +NULL, 0, NULL, 0)) { ret = -1; goto done; } @@ -1116,7 +1140,6 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, struct commit_list *parents = NULL; struct strbuf msg = STRBUF_INIT; struct strbuf commit_tree_label = STRBUF_INIT; -
[GSoC][PATCH v7 19/26] stash: make push to be quiet
There is a change in behaviour with this commit. When there was no initial commit, the shell version of stash would still display a message. This commit makes `push` to not display any message if `--quiet` or `-q` is specified. --- builtin/stash--helper.c | 41 +++-- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index c26cad3d5..4fd79532c 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -1079,7 +1079,7 @@ static int stash_working_tree(struct stash_info *info, static int do_create_stash(int argc, const char **argv, const char *prefix, const char **stash_msg, int include_untracked, - int patch_mode, struct stash_info *info) + int patch_mode, struct stash_info *info, int quiet) { int untracked_commit_option = 0; int ret = 0; @@ -1105,7 +1105,8 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, } if (get_oid("HEAD", >b_commit)) { - fprintf_ln(stderr, "You do not have the initial commit yet"); + if (!quiet) + fprintf_ln(stderr, "You do not have the initial commit yet"); ret = -1; goto done; } else { @@ -1127,7 +1128,8 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, if (write_cache_as_tree(>i_tree, 0, NULL) || commit_tree(commit_tree_label.buf, commit_tree_label.len, >i_tree, parents, >i_commit, NULL, NULL)) { - fprintf_ln(stderr, "Cannot save the current index state"); + if (!quiet) + fprintf_ln(stderr, "Cannot save the current index state"); ret = -1; goto done; } @@ -1135,7 +1137,8 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, if (include_untracked && get_untracked_files(argv, 1, include_untracked, )) { if (save_untracked_files(info, , )) { - printf_ln("Cannot save the untracked files"); + if (!quiet) + printf_ln("Cannot save the untracked files"); ret = -1; goto done; } @@ -1144,14 +1147,16 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, if (patch_mode) { ret = stash_patch(info, argv); if (ret < 0) { - printf_ln("Cannot save the current worktree state"); + if (!quiet) + printf_ln("Cannot save the current worktree state"); goto done; } else if (ret > 0) { goto done; } } else { if (stash_working_tree(info, argv, prefix)) { - printf_ln("Cannot save the current worktree state"); + if (!quiet) + printf_ln("Cannot save the current worktree state"); ret = -1; goto done; } @@ -1176,7 +1181,8 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, if (commit_tree(*stash_msg, strlen(*stash_msg), >w_tree, parents, >w_commit, NULL, NULL)) { - printf_ln("Cannot record working tree state"); + if (!quiet) + printf_ln("Cannot record working tree state"); ret = -1; goto done; } @@ -1208,7 +1214,7 @@ static int create_stash(int argc, const char **argv, const char *prefix) 0); ret = do_create_stash(argc, argv, prefix, _msg, - include_untracked, 0, ); + include_untracked, 0, , 0); if (!ret) printf_ln("%s", oid_to_hex(_commit)); @@ -1261,25 +1267,31 @@ static int do_push_stash(int argc, const char **argv, const char *prefix, return -1; if (!check_changes(argv, include_untracked, prefix)) { - fprintf_ln(stdout, "No local changes to save"); + if (!quiet) + fprintf_ln(stdout, "No local changes to save"); return 0; } if (!reflog_exists(ref_stash) && do_clear_stash()) { - fprintf_ln(stderr, "Cannot initialize stash"); + if (!quiet) + fprintf_ln(stderr, "Cannot initialize stash"); return -1; } if ((ret = do_create_stash(argc, argv, prefix, _msg, - include_untracked, patch_mode, ))) +
[GSoC][PATCH v7 20/26] stash: add tests for `git stash push -q`
This commit introduces more tests for the quiet option of `git stash push`. --- t/t3903-stash.sh | 21 + 1 file changed, 21 insertions(+) diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 8d002a7f2..b78db74ae 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1064,6 +1064,27 @@ test_expect_success 'push: not in the repository errors out' ' test_path_is_file untracked ' +test_expect_success 'push: -q is quiet with changes' ' + >foo && + git stash push -q >output 2>&1 && + test_must_be_empty output +' + +test_expect_success 'push: -q is quiet with no changes' ' + git stash push -q >output 2>&1 && + test_must_be_empty output +' + +test_expect_success 'push: -q is quiet even if there is no initial commit' ' + git init foo_dir && + cd foo_dir && + touch bar && + test_must_fail git stash push -q >output 2>&1 && + test_must_be_empty output && + cd .. && + rm -rf foo_dir +' + test_expect_success 'untracked files are left in place when -u is not given' ' >file && git add file && -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 22/26] stash: convert save to builtin
Add stash save to the helper and delete functions which are no longer needed (`show_help()`, `save_stash()`, `push_stash()`, `create_stash()`, `clear_stash()`, `untracked_files()` and `no_changes()`). --- builtin/stash--helper.c | 48 +++ git-stash.sh| 311 +--- 2 files changed, 50 insertions(+), 309 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index 5c27f5dcf..f54a476e3 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -26,6 +26,8 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" " [-u|--include-untracked] [-a|--all] [-m|--message ]\n" " [--] [...]]"), + N_("git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" + " [-u|--include-untracked] [-a|--all] []"), NULL }; @@ -81,6 +83,12 @@ static const char * const git_stash_helper_push_usage[] = { NULL }; +static const char * const git_stash_helper_save_usage[] = { + N_("git stash--helper save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" + " [-u|--include-untracked] [-a|--all] []"), + NULL +}; + static const char *ref_stash = "refs/stash"; static int quiet; static struct strbuf stash_index_path = STRBUF_INIT; @@ -1445,6 +1453,44 @@ static int push_stash(int argc, const char **argv, const char *prefix) include_untracked, quiet, stash_msg); } +static int save_stash(int argc, const char **argv, const char *prefix) +{ + int i; + int keep_index = -1; + int patch_mode = 0; + int include_untracked = 0; + int quiet = 0; + char *stash_msg = NULL; + struct strbuf alt_stash_msg = STRBUF_INIT; + struct option options[] = { + OPT_SET_INT('k', "keep-index", _index, + N_("keep index"), 1), + OPT_BOOL('p', "patch", _mode, + N_("stash in patch mode")), + OPT_BOOL('q', "quiet", , + N_("quiet mode")), + OPT_BOOL('u', "include-untracked", _untracked, +N_("include untracked files in stash")), + OPT_SET_INT('a', "all", _untracked, + N_("include ignore files"), 2), + OPT_STRING('m', "message", _msg, N_("message"), +N_("stash message")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_save_usage, +0); + + for (i = 0; i < argc; ++i) + strbuf_addf(_stash_msg, "%s ", argv[i]); + + stash_msg = strbuf_detach(_stash_msg, NULL); + + return do_push_stash(0, NULL, prefix, keep_index, patch_mode, +include_untracked, quiet, stash_msg); +} + int cmd_stash__helper(int argc, const char **argv, const char *prefix) { pid_t pid = getpid(); @@ -1485,6 +1531,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) return !!create_stash(argc, argv, prefix); else if (!strcmp(argv[0], "push")) return !!push_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "save")) + return !!save_stash(argc, argv, prefix); usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_helper_usage, options); diff --git a/git-stash.sh b/git-stash.sh index c3146f62a..695f1feba 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -36,314 +36,6 @@ else reset_color= fi -no_changes () { - git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" && - git diff-files --quiet --ignore-submodules -- "$@" && - (test -z "$untracked" || test -z "$(untracked_files "$@")") -} - -untracked_files () { - if test "$1" = "-z" - then - shift - z=-z - else - z= - fi - excl_opt=--exclude-standard - test "$untracked" = "all" && excl_opt= - git ls-files -o $z $excl_opt -- "$@" -} - -clear_stash () { - if test $# != 0 - then - die "$(gettext "git stash clear with parameters is unimplemented")" - fi - if current=$(git rev-parse --verify --quiet $ref_stash) - then - git update-ref -d $ref_stash $current - fi -} - -create_stash () { - stash_msg= - untracked= - while test $# != 0 - do - case "$1" in - -m|--message) - shift - stash_msg=${1?"BUG: create_stash () -m requires an argument"} - ;; - -m*) - stash_msg=${1#-m} - ;; -
[GSoC][PATCH v7 14/26] stash: convert store to builtin
Add stash store to the helper and delete the store_stash function from the shell script. Add the usage string which was forgotten in the shell script. Signed-off-by: Paul-Sebastian Ungureanu --- builtin/stash--helper.c | 52 + git-stash.sh| 43 ++ 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index ec8c38c6f..5ff810f8c 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -20,6 +20,7 @@ static const char * const git_stash_helper_usage[] = { N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] []"), N_("git stash--helper branch []"), N_("git stash--helper clear"), + N_("git stash--helper store [-m|--message ] [-q|--quiet] "), NULL }; @@ -58,6 +59,11 @@ static const char * const git_stash_helper_clear_usage[] = { NULL }; +static const char * const git_stash_helper_store_usage[] = { + N_("git stash--helper store [-m|--message ] [-q|--quiet] "), + NULL +}; + static const char *ref_stash = "refs/stash"; static int quiet; static struct strbuf stash_index_path = STRBUF_INIT; @@ -731,6 +737,50 @@ static int show_stash(int argc, const char **argv, const char *prefix) return 0; } +static int do_store_stash(const char *w_commit, const char *stash_msg, + int quiet) +{ + int ret = 0; + struct object_id obj; + + if (!stash_msg) + stash_msg = xstrdup("Created via \"git stash--helper store\"."); + + ret = get_oid(w_commit, ); + if (!ret) { + ret = update_ref(stash_msg, ref_stash, , NULL, +REF_FORCE_CREATE_REFLOG, +quiet ? UPDATE_REFS_QUIET_ON_ERR : +UPDATE_REFS_MSG_ON_ERR); + } + if (ret && !quiet) + fprintf_ln(stderr, _("Cannot update %s with %s"), + ref_stash, w_commit); + + return ret; +} + +static int store_stash(int argc, const char **argv, const char *prefix) +{ + const char *stash_msg = NULL; + struct option options[] = { + OPT__QUIET(, N_("be quiet, only report errors")), + OPT_STRING('m', "message", _msg, "message", N_("stash message")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, +git_stash_helper_store_usage, +PARSE_OPT_KEEP_UNKNOWN); + + if (argc != 1) { + fprintf(stderr, _("\"git stash--helper store\" requires one argument\n")); + return -1; + } + + return do_store_stash(argv[0], stash_msg, quiet); +} + int cmd_stash__helper(int argc, const char **argv, const char *prefix) { pid_t pid = getpid(); @@ -765,6 +815,8 @@ int cmd_stash__helper(int argc, const char **argv, const char *prefix) return !!list_stash(argc, argv, prefix); else if (!strcmp(argv[0], "show")) return !!show_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "store")) + return !!store_stash(argc, argv, prefix); usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_helper_usage, options); diff --git a/git-stash.sh b/git-stash.sh index 0d05cbc1e..5739c5152 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -191,45 +191,6 @@ create_stash () { die "$(gettext "Cannot record working tree state")" } -store_stash () { - while test $# != 0 - do - case "$1" in - -m|--message) - shift - stash_msg="$1" - ;; - -m*) - stash_msg=${1#-m} - ;; - --message=*) - stash_msg=${1#--message=} - ;; - -q|--quiet) - quiet=t - ;; - *) - break - ;; - esac - shift - done - test $# = 1 || - die "$(eval_gettext "\"$dashless store\" requires one argument")" - - w_commit="$1" - if test -z "$stash_msg" - then - stash_msg="Created via \"git stash store\"." - fi - - git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit - ret=$? - test $ret != 0 && test -z "$quiet" && - die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")" - return $ret -} - push_stash () { keep_index= patch_mode= @@ -308,7 +269,7 @@ push_stash () { clear_stash || die "$(gettext "Cannot initialize stash")" create_stash -m "$stash_msg" -u "$untracked" -- "$@" - store_stash -m
[GSoC][PATCH v7 25/26] stash: replace all `write-tree` child processes with API calls
This commit replaces spawning `git write-tree` with API calls. --- builtin/stash.c | 40 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index 4d5c0d16e..46e76a34e 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -949,9 +949,8 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg) { int ret = 0; struct strbuf untracked_msg = STRBUF_INIT; - struct strbuf out2 = STRBUF_INIT; struct child_process cp = CHILD_PROCESS_INIT; - struct child_process cp2 = CHILD_PROCESS_INIT; + struct index_state state = { NULL }; cp.git_cmd = 1; argv_array_pushl(, "update-index", "--add", @@ -966,15 +965,11 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg) goto done; } - cp2.git_cmd = 1; - argv_array_push(, "write-tree"); - argv_array_pushf(_array, "GIT_INDEX_FILE=%s", -stash_index_path.buf); - if (pipe_command(, NULL, 0, , 0,NULL, 0)) { + if (write_index_as_tree(>u_tree, , stash_index_path.buf, 0, + NULL)) { ret = -1; goto done; } - get_oid_hex(out2.buf, >u_tree); if (commit_tree(untracked_msg.buf, untracked_msg.len, >u_tree, NULL, >u_commit, NULL, NULL)) { @@ -984,7 +979,6 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg) done: strbuf_release(_msg); - strbuf_release(); remove_path(stash_index_path.buf); return ret; } @@ -994,11 +988,10 @@ static struct strbuf patch = STRBUF_INIT; static int stash_patch(struct stash_info *info, const char **argv) { int ret = 0; - struct strbuf out2 = STRBUF_INIT; struct child_process cp0 = CHILD_PROCESS_INIT; struct child_process cp1 = CHILD_PROCESS_INIT; - struct child_process cp2 = CHILD_PROCESS_INIT; struct child_process cp3 = CHILD_PROCESS_INIT; + struct index_state state = { NULL }; remove_path(stash_index_path.buf); @@ -1023,17 +1016,12 @@ static int stash_patch(struct stash_info *info, const char **argv) goto done; } - cp2.git_cmd = 1; - argv_array_push(, "write-tree"); - argv_array_pushf(_array, "GIT_INDEX_FILE=%s", -stash_index_path.buf); - if (pipe_command(, NULL, 0, , 0,NULL, 0)) { + if (write_index_as_tree(>w_tree, , stash_index_path.buf, 0, + NULL)) { ret = -1; goto done; } - get_oid_hex(out2.buf, >w_tree); - cp3.git_cmd = 1; argv_array_pushl(, "diff-tree", "-p", "HEAD", oid_to_hex(>w_tree), "--", NULL); @@ -1046,7 +1034,6 @@ static int stash_patch(struct stash_info *info, const char **argv) } done: - strbuf_release(); remove_path(stash_index_path.buf); return ret; } @@ -1056,11 +1043,10 @@ static int stash_working_tree(struct stash_info *info, { int ret = 0; struct child_process cp2 = CHILD_PROCESS_INIT; - struct child_process cp3 = CHILD_PROCESS_INIT; - struct strbuf out3 = STRBUF_INIT; struct argv_array args = ARGV_ARRAY_INIT; struct strbuf diff_output = STRBUF_INIT; struct rev_info rev; + struct index_state state = { NULL }; set_alternate_index_output(stash_index_path.buf); if (reset_tree(>i_tree, 0, 0)) { @@ -1103,20 +1089,18 @@ static int stash_working_tree(struct stash_info *info, goto done; } - cp3.git_cmd = 1; - argv_array_push(, "write-tree"); - argv_array_pushf(_array, "GIT_INDEX_FILE=%s", -stash_index_path.buf); - if (pipe_command(, NULL, 0, , 0,NULL, 0)) { + if (write_index_as_tree(>w_tree, , stash_index_path.buf, 0, + NULL)) { + ret = -1; goto done; } - get_oid_hex(out3.buf, >w_tree); + discard_cache(); + read_cache(); done: UNLEAK(rev); - strbuf_release(); argv_array_clear(); object_array_clear(); strbuf_release(_output); -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 16/26] stash: replace spawning a "read-tree" process
Instead of spawning a child process, make use of `reset_tree()` function already implemented in `stash-helper.c`. --- builtin/stash--helper.c | 9 +++-- 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index a4e57899b..887b78d05 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -984,21 +984,18 @@ static int stash_patch(struct stash_info *info, const char **argv) static int stash_working_tree(struct stash_info *info, const char **argv) { int ret = 0; - struct child_process cp0 = CHILD_PROCESS_INIT; struct child_process cp1 = CHILD_PROCESS_INIT; struct child_process cp2 = CHILD_PROCESS_INIT; struct child_process cp3 = CHILD_PROCESS_INIT; struct strbuf out1 = STRBUF_INIT; struct strbuf out3 = STRBUF_INIT; - cp0.git_cmd = 1; - argv_array_push(, "read-tree"); - argv_array_pushf(, "--index-output=%s", stash_index_path.buf); - argv_array_pushl(, "-m", oid_to_hex(>i_tree), NULL); - if (run_command()) { + set_alternate_index_output(stash_index_path.buf); + if (reset_tree(>i_tree, 0, 0)) { ret = -1; goto done; } + set_alternate_index_output(".git/index"); cp1.git_cmd = 1; argv_array_pushl(, "diff-index", "--name-only", "-z", -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 21/26] stash: replace spawning `git ls-files` child process
This commit replaces spawning `git ls-files` child process with API calls to get the untracked files. --- builtin/stash--helper.c | 49 +++-- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/builtin/stash--helper.c b/builtin/stash--helper.c index 4fd79532c..5c27f5dcf 100644 --- a/builtin/stash--helper.c +++ b/builtin/stash--helper.c @@ -813,27 +813,42 @@ static int store_stash(int argc, const char **argv, const char *prefix) /* * `out` will be filled with the names of untracked files. The return value is: * - * < 0 if there was a bug (any arg given outside the repo will be detected - * by `setup_revision()`) * = 0 if there are not any untracked files * > 0 if there are untracked files */ -static int get_untracked_files(const char **argv, int line_term, +static int get_untracked_files(const char **argv, const char *prefix, int include_untracked, struct strbuf *out) { - struct child_process cp = CHILD_PROCESS_INIT; - cp.git_cmd = 1; - argv_array_pushl(, "ls-files", "-o", NULL); - if (line_term) - argv_array_push(, "-z"); + int max_len; + int i; + char *seen; + struct dir_struct dir; + struct pathspec pathspec; + + memset(, 0, sizeof(dir)); if (include_untracked != 2) - argv_array_push(, "--exclude-standard"); - argv_array_push(, "--"); - if (argv) - argv_array_pushv(, argv); + setup_standard_excludes(); - if (pipe_command(, NULL, 0, out, 0, NULL, 0)) - return -1; + parse_pathspec(, 0, + PATHSPEC_PREFER_FULL, + prefix, argv); + seen = xcalloc(pathspec.nr, 1); + + max_len = fill_directory(, the_repository->index, ); + for (i = 0; i < dir.nr; i++) { + struct dir_entry *ent = dir.entries[i]; + if (!dir_path_match(ent, , max_len, seen)) { + free(ent); + continue; + } + strbuf_addf(out, "%s\n", ent->name); + free(ent); + } + + free(dir.entries); + free(dir.ignored); + clear_directory(); + free(seen); return out->len; } @@ -888,7 +903,7 @@ static int check_changes(const char **argv, int include_untracked, goto done; } - if (include_untracked && get_untracked_files(argv, 0, + if (include_untracked && get_untracked_files(argv, prefix, include_untracked, )) ret = 1; @@ -908,7 +923,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg, struct child_process cp2 = CHILD_PROCESS_INIT; cp.git_cmd = 1; - argv_array_pushl(, "update-index", "-z", "--add", + argv_array_pushl(, "update-index", "--add", "--remove", "--stdin", NULL); argv_array_pushf(_array, "GIT_INDEX_FILE=%s", stash_index_path.buf); @@ -1134,7 +1149,7 @@ static int do_create_stash(int argc, const char **argv, const char *prefix, goto done; } - if (include_untracked && get_untracked_files(argv, 1, + if (include_untracked && get_untracked_files(argv, prefix, include_untracked, )) { if (save_untracked_files(info, , )) { if (!quiet) -- 2.18.0.573.g56500d98f
[GSoC][PATCH v7 02/26] stash: improve option parsing test coverage
From: Joel Teichroeb In preparation for converting the stash command incrementally to a builtin command, this patch improves test coverage of the option parsing. Both for having too many parameters, or too few. Signed-off-by: Joel Teichroeb Signed-off-by: Paul-Sebastian Ungureanu --- t/t3903-stash.sh | 35 +++ 1 file changed, 35 insertions(+) diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 1f871d3cc..af7586d43 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -444,6 +444,36 @@ test_expect_failure 'stash file to directory' ' test foo = "$(cat file/file)" ' +test_expect_success 'giving too many ref arguments does not modify files' ' + git stash clear && + test_when_finished "git reset --hard HEAD" && + echo foo >file2 && + git stash && + echo bar >file2 && + git stash && + test-tool chmtime =123456789 file2 && + for type in apply pop "branch stash-branch" + do + test_must_fail git stash $type stash@{0} stash@{1} 2>err && + test_i18ngrep "Too many revisions" err && + test 123456789 = $(test-tool chmtime -g file2) || return 1 + done +' + +test_expect_success 'drop: too many arguments errors out (does nothing)' ' + git stash list >expect && + test_must_fail git stash drop stash@{0} stash@{1} 2>err && + test_i18ngrep "Too many revisions" err && + git stash list >actual && + test_cmp expect actual +' + +test_expect_success 'show: too many arguments errors out (does nothing)' ' + test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out && + test_i18ngrep "Too many revisions" err && + test_must_be_empty out +' + test_expect_success 'stash create - no changes' ' git stash clear && test_when_finished "git reset --hard HEAD" && @@ -479,6 +509,11 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' ' test $(git ls-files --modified | wc -l) -eq 1 ' +test_expect_success 'stash branch complains with no arguments' ' + test_must_fail git stash branch 2>err && + test_i18ngrep "No branch name specified" err +' + test_expect_success 'stash show format defaults to --stat' ' git stash clear && test_when_finished "git reset --hard HEAD" && -- 2.18.0.573.g56500d98f
Re: [PATCH 11/11] builtin rebase: support `git rebase `
Hi Duy, On Wed, 8 Aug 2018, Duy Nguyen wrote: > On Wed, Aug 8, 2018 at 3:55 PM Pratik Karki wrote: > > > > diff --git a/builtin/rebase.c b/builtin/rebase.c > > index 63634210c0..b2ddfa8dbf 100644 > > --- a/builtin/rebase.c > > +++ b/builtin/rebase.c > > @@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char > > *prefix) > > } > > if (get_oid("HEAD", _head)) > > die(_("Could not resolve HEAD to a revision")); > > - } > > + } else > > + BUG("unexpected number of arguments left to parse"); > > Does this mean "git base one two three" triggers this BUG? If so, this > should be a die() instead. I did not real the full source code, so > maybe this case is already caught higher up. As you can see from https://github.com/git/git/blob/v2.18.0/git-rebase.sh#L615 the original, Unix shell script version of `git rebase` also says "BUG" here. And if you care to look at https://github.com/git/git/blob/3358abdcb/builtin/rebase.c#L870-L872 you will see that there is a proper check for the correct amount of command-line parameters. So at this point, it would indeed indicate a bug if the `argc` had an unexpected value. Ciao, Dscho
Re: [PATCH 08/11] builtin rebase: support --force-rebase
On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki wrote: > @@ -551,10 +560,21 @@ int cmd_rebase(int argc, const char **argv, const char > *prefix) [...] > ; /* be quiet */ > else if (!strcmp(branch_name, "HEAD") && > - resolve_ref_unsafe("HEAD", 0, NULL, )) > +resolve_ref_unsafe("HEAD", 0, NULL, )) This line is changing only the indentation whitespace? Would it make sense to have it in the previous patch?
Re: [PATCH v2 4/4] unpack-trees: cheaper index update when walking by cache-tree
On Sun, Jul 29, 2018 at 3:36 AM Nguyễn Thái Ngọc Duy wrote: > > With the new cache-tree, we could mostly avoid I/O (due to odb access) > the code mostly becomes a loop of "check this, check that, add the > entry to the index". We could skip a couple checks in this giant loop > to go faster: > > - We know here that we're copying entries from the source index to the > result one. All paths in the source index must have been validated > at load time already (and we're not taking strange paths from tree > objects) which means we can skip verify_path() without compromise. > > - We also know that D/F conflicts can't happen for all these entries > (since cache-tree and all the trees are the same) so we can skip > that as well. > > This gives rather nice speedups for "unpack trees" rows where "unpack > trees" time is now cut in half compared to when > traverse_by_cache_tree() is added, or 1/7 of the original "unpack > trees" time. > >baseline cache-treethis patch > >0.018239226 0.019365414 0.020519621 s: read cache .git/index >0.052541655 0.049605548 0.048814384 s: preload index >0.001537598 0.001571695 0.001575382 s: refresh index >0.168167768 0.049677212 0.024719308 s: unpack trees >0.002897186 0.002845256 0.00280 s: update worktree after a merge >0.131661745 0.136597522 0.134891617 s: repair cache-tree >0.075389117 0.075422517 0.074832291 s: write index, changed mask = 2a >0.111702023 0.032813253 0.008616479 s: unpack trees >0.23245 0.22002 0.26630 s: update worktree after a merge >0.111793866 0.032933140 0.008714071 s: diff-index >0.587933288 0.398924370 0.380452871 s: git command: > /home/pclouds/w/git/git > > Total saving of this new patch looks even less impressive, now that > time spent in unpacking trees is so small. Which is why the next > attempt should be on that "repair cache-tree" line. > > Signed-off-by: Nguyễn Thái Ngọc Duy > --- > cache.h| 1 + > read-cache.c | 3 ++- > unpack-trees.c | 27 +++ > unpack-trees.h | 1 + > 4 files changed, 31 insertions(+), 1 deletion(-) > > diff --git a/cache.h b/cache.h > index 8b447652a7..e6f7ee4b64 100644 > --- a/cache.h > +++ b/cache.h > @@ -673,6 +673,7 @@ extern int index_name_pos(const struct index_state *, > const char *name, int name > #define ADD_CACHE_JUST_APPEND 8/* Append only; > tree.c::read_tree() */ > #define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */ > #define ADD_CACHE_KEEP_CACHE_TREE 32 /* Do not invalidate cache-tree */ > +#define ADD_CACHE_SKIP_VERIFY_PATH 64 /* Do not verify path */ > extern int add_index_entry(struct index_state *, struct cache_entry *ce, int > option); > extern void rename_index_entry_at(struct index_state *, int pos, const char > *new_name); > > diff --git a/read-cache.c b/read-cache.c > index e865254bea..b0b5df5de7 100644 > --- a/read-cache.c > +++ b/read-cache.c > @@ -1170,6 +1170,7 @@ static int add_index_entry_with_check(struct > index_state *istate, struct cache_e > int ok_to_add = option & ADD_CACHE_OK_TO_ADD; > int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE; > int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK; > + int skip_verify_path = option & ADD_CACHE_SKIP_VERIFY_PATH; > int new_only = option & ADD_CACHE_NEW_ONLY; > > if (!(option & ADD_CACHE_KEEP_CACHE_TREE)) > @@ -1210,7 +1211,7 @@ static int add_index_entry_with_check(struct > index_state *istate, struct cache_e > > if (!ok_to_add) > return -1; > - if (!verify_path(ce->name, ce->ce_mode)) > + if (!skip_verify_path && !verify_path(ce->name, ce->ce_mode)) > return error("Invalid path '%s'", ce->name); > > if (!skip_df_check && > diff --git a/unpack-trees.c b/unpack-trees.c > index c33ebaf001..dc62afd968 100644 > --- a/unpack-trees.c > +++ b/unpack-trees.c > @@ -201,6 +201,7 @@ static int do_add_entry(struct unpack_trees_options *o, > struct cache_entry *ce, > > ce->ce_flags = (ce->ce_flags & ~clear) | set; > return add_index_entry(>result, ce, > + o->extra_add_index_flags | >ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); > } > > @@ -701,6 +702,24 @@ static int traverse_by_cache_tree(int pos, int > nr_entries, int nr_names, > if (!o->merge) > BUG("We need cache-tree to do this optimization"); > > + /* > +* Try to keep add_index_entry() as fast as possible since > +* we're going to do a lot of them. > +* > +* Skipping verify_path() should totally be safe because these > +* paths are from the source index, which must have been > +* verified. > +* > +* Skipping D/F and cache-tree validation checks
Re: [PATCH 2/2] repack: repack promisor objects if -a or -A is set
> But what to delta against what else is determined by the pathname > info, which is now lost by enumerating objects without tree/history > walking. By giving phoney pathnames to objects while enumerating > them in offset order and giving similar pathnames to objects closer > to each other, I was hoping that better bases will likely to be in > the same window. The order in which objects are prepared and fed to > try_delta() is "group by type, and then sort by path-hash (major > key) and size (descending order, used as minor key)", so that > the largest among similarly named blobs is stored as base and other > blobs with similar name try to become delta against that one. Thanks for the patient explanation - I think I see it now. If we enumerate in pack order and pass " " instead of "" to pack-objects in such a way that we make pack-objects sort by {type -> pack-order -> size} instead of {type -> size}, we can hopefully get better deltas. I'll write a NEEDSWORK similar to this. I'll send a reroll later today.
Re: Page content is wider than view window
Eric Sunshine writes: > The git-scm.com website is maintained as a distinct project[1] at > Github; it is not directly related to the Git project itself (to which > you sent this email). A good way to voice a concern or make a > suggestion about the website is either to open an issue[2] or submit a > pull request[3] if you have a specific change in mind. > > The Pro Git book (if that's what you're reading) is also a distinct > project[4], not directly related to the Git project. You can likewise > open an issue[5] or submit a pull request[6] if needed. > > [...] Ah, that was completely discoverable through the website. My mistake, thank you for redirect. -- Brady
Re: [PATCH v3 0/4] Speed up unpack_trees()
Junio C Hamano writes: > Junio C Hamano writes: > >> not used behind is *not* OK. And lack of restoring the bottom in >> the new codepath makes me suspect exactly such a bug _after_ the >> traversal exits the subtree we are using this new optimization in >> and moves on. > > Hmph, thinking about this further, I cannot convince myself that > lack of bottom adjustment can lead to a triggerable bug. The only > case that a subtree traversal need to skip some unpacked entries in > the index and then revisit them by rewinding, e.g. entries "t-i" and > "t-j" that are left unprocessed while entries "t/1", "t/2", etc. are > processed, in the illustration of da165f47 ("unpack-trees.c: prepare > for looking ahead in the index", 2010-01-07), is when one of the > trees have a non-tree with the same name as the subtree we are > trying to descend into, and as long as we know all trees have the > thing as a tree, I do not think of a case where such ordering > inversion would get in the way. One more, and hopefully the final, note. A paranoid may be soothed by a simple "cache_bottom must match pos at this point" at the beginning of the optimized traversal. Just like you already have an assert to ensure that pos points at the first entry in the directory in index_pos_by_traverse_info(), there should not be any unused entry in the index before that entry and the bottom pointer must be pointing at it. It is a cheap check, and if violated, would indicate that the above "I do not think of a case ..." was incomplete. > That was the only thing I found questionable in 2/4, which is the > most important piece in the series, so we probably are OK. > > Thanks for working on this one.
Re: [PATCH 04/11] builtin rebase: support --quiet
On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki wrote: > > This commit introduces a rebase option `--quiet`. While `--quiet` is > commonly perceived as opposite to `--verbose`, this is not the case for > the rebase command: both `--quiet` and `--verbose` default to `false` if > neither `--quiet` nor `--verbose` is present. > > This commit goes further and introduces `--no-quiet` which is the > contrary of `--quiet` and it's introduction doesn't modify any > behaviour. > > Note: The `flags` field in `rebase_options` will accumulate more bits in > subsequent commits, in particular a verbose and a diffstat flag. And as > --quoet inthe shell scripted version of the rebase command switches off --quote in the (in case a resend is needed)
Re: [PATCH v3 3/4] unpack-trees: reduce malloc in cache-tree walk
On Fri, Aug 3, 2018 at 10:39 PM Nguyễn Thái Ngọc Duy wrote: > > This is a micro optimization that probably only shines on repos with > deep directory structure. Instead of allocating and freeing a new > cache_entry in every iteration, we reuse the last one and only update > the parts that are new each iteration. > > Signed-off-by: Nguyễn Thái Ngọc Duy > Signed-off-by: Junio C Hamano > --- > unpack-trees.c | 29 - > 1 file changed, 20 insertions(+), 9 deletions(-) > > diff --git a/unpack-trees.c b/unpack-trees.c > index ba3d2e947e..c8defc2015 100644 > --- a/unpack-trees.c > +++ b/unpack-trees.c > @@ -694,6 +694,8 @@ static int traverse_by_cache_tree(int pos, int > nr_entries, int nr_names, > { > struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, }; > struct unpack_trees_options *o = info->data; > + struct cache_entry *tree_ce = NULL; > + int ce_len = 0; > int i, d; > > if (!o->merge) > @@ -708,30 +710,39 @@ static int traverse_by_cache_tree(int pos, int > nr_entries, int nr_names, > * in the first place. > */ > for (i = 0; i < nr_entries; i++) { > - struct cache_entry *tree_ce; > - int len, rc; > + int new_ce_len, len, rc; > > src[0] = o->src_index->cache[pos + i]; > > len = ce_namelen(src[0]); > - tree_ce = xcalloc(1, cache_entry_size(len)); > + new_ce_len = cache_entry_size(len); > + > + if (new_ce_len > ce_len) { > + new_ce_len <<= 1; > + tree_ce = xrealloc(tree_ce, new_ce_len); > + memset(tree_ce, 0, new_ce_len); > + ce_len = new_ce_len; > + > + tree_ce->ce_flags = create_ce_flags(0); > + > + for (d = 1; d <= nr_names; d++) > + src[d] = tree_ce; > + } > > tree_ce->ce_mode = src[0]->ce_mode; > - tree_ce->ce_flags = create_ce_flags(0); > tree_ce->ce_namelen = len; > oidcpy(_ce->oid, [0]->oid); > memcpy(tree_ce->name, src[0]->name, len + 1); > > - for (d = 1; d <= nr_names; d++) > - src[d] = tree_ce; > - > rc = call_unpack_fn((const struct cache_entry * const *)src, > o); > - free(tree_ce); > - if (rc < 0) > + if (rc < 0) { > + free(tree_ce); > return rc; > + } > > mark_ce_used(src[0], o); > } > + free(tree_ce); > if (o->debug_unpack) > printf("Unpacked %d entries from %s to %s using cache-tree\n", >nr_entries, > -- > 2.18.0.656.gda699b98b3 Seems reasonable, when we really do have to invoke call_unpack_fn. I'm still curious if there are reasons why we couldn't just skip that call (at least when o->fn is one of {oneway_merge, twoway_merge, threeway_merge, bind_merge}), but I already brought that up in my comments on patch 2.
Re: Help with "fatal: unable to read ...." error during GC?
On Wed, Aug 08, 2018 at 01:35:30PM -0400, Paul Smith wrote: > Thanks for the note! Unhappily for me none of these operations seem to > find any actionable problems... > [...] Drat. One other option is that it _could_ be related to the "old unreachable objects that are reachable from recent unreachable objects should be kept" code. That's supposed to quietly ignore broken links in unreachable objects, but there could be a bug. Let's narrow it down first and make sure we're dying where I expect. Can you try: GIT_TRACE=1 git gc and confirm the program running when the fatal error is produced? >From what you've shown it's going to be git-repack, but what I'm not clear on is whether it is repack itself that is complaining, or the pack-objects process it spawns. I'd guess the latter. If so, can you try running it under gdb and getting a stack trace? Something like: gdb git [and then inside gdb...] set args pack-objects --all --reflog --indexed-objects foo
Re: [PATCH v3 2/4] unpack-trees: optimize walking same trees with cache-tree
On Fri, Aug 3, 2018 at 10:39 PM Nguyễn Thái Ngọc Duy wrote: > From: Duy Nguyen > > In order to merge one or many trees with the index, unpack-trees code > walks multiple trees in parallel with the index and performs n-way > merge. If we find out at start of a directory that all trees are the > same (by comparing OID) and cache-tree happens to be available for > that directory as well, we could avoid walking the trees because we > already know what these trees contain: it's flattened in what's called > "the index". This is cool. > The upside is of course a lot less I/O since we can potentially skip > lots of trees (think subtrees). We also save CPU because we don't have > to inflate and the apply deltas. The downside is of course more s/and the apply/and apply the/ > fragile code since the logic in some functions are now duplicated > elsewhere. > > "checkout -" with this patch on gcc.git: > > baseline new > > 0.018239226 0.019365414 s: read cache .git/index > 0.052541655 0.049605548 s: preload index > 0.001537598 0.001571695 s: refresh index > 0.168167768 0.049677212 s: unpack trees > 0.002897186 0.002845256 s: update worktree after a merge > 0.131661745 0.136597522 s: repair cache-tree > 0.075389117 0.075422517 s: write index, changed mask = 2a > 0.111702023 0.032813253 s: unpack trees > 0.23245 0.22002 s: update worktree after a merge > 0.111793866 0.032933140 s: diff-index > 0.587933288 0.398924370 s: git command: /home/pclouds/w/git/git > > Another measurement from Ben's running "git checkout" with over 500k > trees (on the whole series): > > baselinenew > -- > 0.535510167 0.556558733 s: read cache .git/index > 0.3057373 0.3147105 s: initialize name hash > 0.0184082 0.023558433 s: preload index > 0.086910967 0.089085967 s: refresh index > 7.889590767 2.191554433 s: unpack trees > 0.120760833 0.131941267 s: update worktree after a merge > 2.2583504 2.572663167 s: repair cache-tree > 0.8916137 0.959495233 s: write index, changed mask = 28 > 3.405199233 0.2710663 s: unpack trees > 0.000999667 0.0021554 s: update worktree after a merge > 3.4063306 0.273318333 s: diff-index > 16.9524923 9.462943133 s: git command: git.exe checkout > > This command calls unpack_trees() twice, the first time on 2way merge > and the second 1way merge. In both times, "unpack trees" time is > reduced to one third. Overall time reduction is not that impressive of > course because index operations take a big chunk. And there's that > repair cache-tree line. > > Signed-off-by: Nguyễn Thái Ngọc Duy > --- > unpack-trees.c | 117 + > 1 file changed, 117 insertions(+) > > diff --git a/unpack-trees.c b/unpack-trees.c > index a32ddee159..ba3d2e947e 100644 > --- a/unpack-trees.c > +++ b/unpack-trees.c > @@ -644,6 +644,102 @@ static inline int are_same_oid(struct name_entry > *name_j, struct name_entry *nam > return name_j->oid && name_k->oid && !oidcmp(name_j->oid, > name_k->oid); > } > > +static int all_trees_same_as_cache_tree(int n, unsigned long dirmask, > + struct name_entry *names, > + struct traverse_info *info) > +{ > + struct unpack_trees_options *o = info->data; > + int i; > + > + if (!o->merge || dirmask != ((1 << n) - 1)) > + return 0; > + > + for (i = 1; i < n; i++) > + if (!are_same_oid(names, names + i)) > + return 0; > + > + return cache_tree_matches_traversal(o->src_index->cache_tree, names, > info); > +} I was curious whether this could also be extended in the case of a merge; as long as HEAD and MERGE have the same tree, even if the base commit doesn't match, we can still just use the tree from HEAD which should be in the current index/cache_tree. However, it'd be a somewhat odd history for HEAD and MERGE to match on some significantly sized tree when the base commit doesn't also match. > + > +static int index_pos_by_traverse_info(struct name_entry *names, > + struct traverse_info *info) > +{ > + struct unpack_trees_options *o = info->data; > + int len = traverse_path_len(info, names); > + char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */); > + int pos; > + > + make_traverse_path(name, info, names); > + name[len++] = '/'; > + name[len] = '\0'; > + pos = index_name_pos(o->src_index, name, len); > + if (pos >= 0) > + BUG("This is a directory and should not exist in index"); > + pos = -pos - 1; > +
Re: [PATCH v3 0/4] Speed up unpack_trees()
Junio C Hamano writes: > not used behind is *not* OK. And lack of restoring the bottom in > the new codepath makes me suspect exactly such a bug _after_ the > traversal exits the subtree we are using this new optimization in > and moves on. Hmph, thinking about this further, I cannot convince myself that lack of bottom adjustment can lead to a triggerable bug. The only case that a subtree traversal need to skip some unpacked entries in the index and then revisit them by rewinding, e.g. entries "t-i" and "t-j" that are left unprocessed while entries "t/1", "t/2", etc. are processed, in the illustration of da165f47 ("unpack-trees.c: prepare for looking ahead in the index", 2010-01-07), is when one of the trees have a non-tree with the same name as the subtree we are trying to descend into, and as long as we know all trees have the thing as a tree, I do not think of a case where such ordering inversion would get in the way. That was the only thing I found questionable in 2/4, which is the most important piece in the series, so we probably are OK. Thanks for working on this one.
Re: Help with "fatal: unable to read ...." error during GC?
On Wed, 2018-08-08 at 12:06 -0400, Jeff King wrote: > I'd have expected fsck to find it, too. However, looking at the code, > I'm not convinced that fsck is actually considering detached worktree > heads properly, either. Try: > > git rev-list --all --reflog --objects >/dev/null > > which I know checks worktrees correctly. I'd expect that to fail. > > If it does, then we need to narrow down which worktree is corrupt. > Perhaps something like: > > git worktree list | > while read worktree head junk; do > git rev-list --objects $head >/dev/null || > echo "$worktree seems corrupt" > done Thanks for the note! Unhappily for me none of these operations seem to find any actionable problems... $ git rev-list --all --reflog --objects >/dev/null warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits warning: reflog of 'HEAD' references pruned commits $ echo $? 0 $ git worktree list | while read wt head junk; do \ git rev-list --objects $head >/dev/null || echo "$wt seems corrupt"; \ done $ Just to be sure I updated the loop above to echo $wt and $head and they were correct. I also re-ran git gc after the above and still got the original error output so it didn't magically fix itself :).
Re: [PATCH v3 0/4] Speed up unpack_trees()
Duy Nguyen writes: > On Mon, Aug 6, 2018 at 5:48 PM Junio C Hamano wrote: >> > I've also checked about the lookahead thing in unpack_trees() to see >> > if we accidentally break something there, which is my biggest worry. >> > See [1] and [2] for context, but I believe since we can't have D/F >> > conflicts, the situation where lookahead is needed will not occur. So >> > we should be safe. I think you would want the same "switch cache-bottom before descending into a subdirectory, and then restore cache-bottom after traversal comes back" dance that is done for the normal tree traversal case to happen. bottom = switch_cache_bottom(); ret = traverse_trees(n, t, ); restore_cache_bottom(, bottom); During your walk of the index and the trees that are known to be in sync, there is little reason to worry about the cache_bottom, which is advanced by calling mark_ce_used() in traverse_by_cache_tree(). Where it matters is what happens after the traversal comes back out of the subtree. find_cache_pos() uses the bottom pointer so that it does not have to go back to far to find an index entry that has not been used to match with the entries from the trees (which are not sorted exactly the same way as the index, unfortunately), so forgetting to advance the bottom pointer while correctly marking a ce as "used" is OK (i.e. hurts performance but not correctness), but advancing the bottom pointer too much and leaving entries that are not used behind is *not* OK. And lack of restoring the bottom in the new codepath makes me suspect exactly such a bug _after_ the traversal exits the subtree we are using this new optimization in and moves on. Imagine we are iterating over the top-level of the trees, and found a subtree in them. There may be some index entries before the first path in this subtree that are not yet marked as "used", the earliest of which is pointed at by the cache_bottom pointer. Before descending into the subtree (and start consuming the entry with the first in this subtree from the index), we stash away the current cache_bottom, and then start walking the subtree. While we are in that subtree, the cache_bottom starts from the first name in the subtree and increments, as we _know_ the entry at the old cache_bottom is outside this subtree and will not match any entry form the subtree. Then when the traversal returns, all index entries within the "subtree/" path will be marked "used". At that point, when we continue to scan the top-level of the trees, we need to restore the cache_bottom, so that we do not forget entries that we knew we needed to scan eventually, if there was any.
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
On Wed, Aug 8, 2018 at 6:05 AM Johannes Schindelin wrote: > > [...] > > > diff --git a/Makefile b/Makefile > > > --- a/Makefile > > > +++ b/Makefile > > > > The line starting with --- is red (default removed color) and the line > > with +++ is green (default add color). > > > > Ideally these two lines and the third line above starting with "diff --git" > > would render in GIT_COLOR_BOLD ("METAINFO"). > > I agree that is not the best coloring here, but as you remarked elsewhere, > it would require content-aware dual coloring, and I am loathe to try to > implement that for two reasons: 1) it would take most likely a long time > to design and implement that, and 2) I don't have that time. > > So I would like to declare that good enough is good enough in this case. I anticipated this answer, so I wrote some patches myself, starting at https://public-inbox.org/git/20180804015317.182683-1-sbel...@google.com/ specifically https://public-inbox.org/git/20180804015317.182683-5-sbel...@google.com/ I plan on resending these on top of your resend (if any) at a later convenient time for both you and Junio, as noted in https://public-inbox.org/git/cagz79kznvesvpicnu7lxkrchurqgvesfvg3dl5o_2kpvyrw...@mail.gmail.com/ > > > > 3: 076e1192d ! 3: 4e3fb47a1 range-diff: first rudimentary > > > implementation > > > @@ -4,7 +4,7 @@ > > > > > > At this stage, `git range-diff` can determine corresponding > > > commits > > > of two related commit ranges. This makes use of the recently > > > introduced > > > -implementation of the Hungarian algorithm. > > > +implementation of the linear assignment algorithm. > > > > > > The core of this patch is a straight port of the ideas of > > > tbdiff, the > > > apparently dormant project at https://github.com/trast/tbdiff. > > > @@ -51,19 +51,17 @@ > > > + int res = 0; > > > + struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT; > > > > > > -- argc = parse_options(argc, argv, NULL, options, > > > -- builtin_range_diff_usage, 0); > > > -+ argc = parse_options(argc, argv, NULL, options, > > > builtin_range_diff_usage, > > > -+ 0); > > > + argc = parse_options(argc, argv, NULL, options, > > > + builtin_range_diff_usage, 0); > > > > This is really nice in colors when viewed locally. > > > > > 16: dfa7b1e71 < -: - range-diff --dual-color: work around > > > bogus white-space warning > > > -: - > 16: f4252f2b2 range-diff --dual-color: fix bogus > > > white-space warning > > > > Ah; here my initial assumption of only reviewing the range-diff breaks down > > now. > > I'll dig into patch 16 separately. > > Right. This was an almost complete rewrite, and then next iteration will > hopefully bring another complete rewrite: disabling whitespace warnings in > dual color mode. > > > Maybe it is worth having an option to expand all "new" patches. > > Sure. > > And I also have a use case for --left-only/--right-only. > > And I also have a strong use case (and so does Junio, it seems, or for > that matter, anybody contributing to Git due to Junio's insistence on > signing off on each patch, rather than on the merge commit) for something > like --ignore-lines=. > > And you probably guess what I will say next: these features will make for > really fantastic patch series *on top* of mine. There really is no good > reason to delay the current patch series just to cram more features into > it that had not been planned in the first place. Yes, I agree. I am unsure about the current state of your series, though; Junio thinks (expects?) a resend, whereas you seem to call it good enough but also said (some time back) that you want to resend due to Thomas feedback. I do have 2 series on top of the current range-diff. * The first (queued by Junio as origin/sb/range-diff-colors) adds a basic test for colors and improves diff.c readability * The second (linked above) changes colors for some lines. I do not want to build more on top as long as I do not know if you resend (and how much it'll change) Thanks, Stefan
Re: [PATCH v3 0/4] Speed up unpack_trees()
On 8/6/2018 2:59 PM, Junio C Hamano wrote: Duy Nguyen writes: We require the unpacked entry from all input trees to be a tree objects (the dirmask thing), so if one tree has 't' as a file, Ah, OK, this is still part of that "all the trees match cache tree so we walk the index instead" optimization. I forgot about that. I ran this set of patches through the VFS For Git set of functional tests as well as our performance test suite (my earlier perf numbers were from manual testing). All the functional tests pass and the performance tests are looking _very_ promising. Checkout times are impacted most and on average drop from 20.96 seconds to 11.63 seconds for a 45% savings. Merge times drop from 19.44 seconds to 12.88 for a 34% savings. Rebase times drop from 26.78 seconds to 20.72 for a 23% savings. Overall, I'm looking forward to a good review of the patches and seeing them get merged as soon as they are ready.
Re: [PATCH 2/2] remote-curl: remove spurious period
On Wed, Aug 8, 2018 at 4:52 AM Johannes Schindelin via GitGitGadget wrote: > > We should not interrupt. sentences in the middle. I love this. commit message. :-) ... > /* The client backend isn't giving us compressed data so > -* we can try to deflate it ourselves, this may save on. > +* we can try to deflate it ourselves, this may save on > * the transfer time.
Re: [PATCH 2/2] repack: repack promisor objects if -a or -A is set
Jonathan Tan writes: >> Just a note (and a request-for-sanity-check) and not meant to be a >> request to update the code, but with a still-in-pu 4b757a40 ("Use >> remote_odb_get_direct() and has_remote_odb()", 2018-08-02) in >> flight, repository_format_partial_clone is now gone. >> ... > Thanks for the heads-up. This makes me realize that even the current > precondition (repository_format_partial_clone) is not an exact one - I > should only be doing this if I know that there is at least one promisor > object (if not, in the rare case that a repo is configured to be partial > but does not have any promisor objects, repack will generate an empty > packfile). In the next reroll, I'll take care of this, which should > avoid this merge issue too. I think that it is a good change, regardless of the semantic conflict with any other topic, to look at the .promisor packs and not look at the partial clone extension for this "do we want to coalesce .promisor packs into one?" application. You only want to do so when you have two or more such packs; being in partial clone might be a preconditon for having such packs, but it is overly loose. In any case, remote-odb topic is turning out to be more trouble than I originally thought; in the same change that removes the variable, it actually removes the support for a repository configured with a partial clone extension, and causes a hard error in t0410, breaking test of 'pu'. I do not know how many people actually use partial clone right now, but their repositories would be broken the same way when they update Git, if we merge that topic in its current shape. I am dropping it from 'pu' for today's integration cycle.
[PATCHv4 0/5] Simple fixes to t7406
Changes since v3, all suggested/endorsed by Junio (range-diff at the end): - Moved the actual fix from being last patch in the series to the first (other patches in this series are just test code cleanups) - Anchored regexes to avoid matching another filename as a substring - Add test_path_exists() to test-lib-function.sh and use it (we had test_path_is_dir, test_path_is_file, and test_path_is_missing, but not simple test_path_exists) Elijah Newren (5): t7406: fix call that was failing for the wrong reason t7406: simplify by using diff --name-only instead of diff --raw t7406: avoid having git commands upstream of a pipe t7406: prefer test_* helper functions to test -[feds] t7406: avoid using test_must_fail for commands other than git t/t7406-submodule-update.sh | 37 +++-- t/test-lib-functions.sh | 8 2 files changed, 31 insertions(+), 14 deletions(-) -: -- > 1: 5f257af6c8 t7406: fix call that was failing for the wrong reason 1: 3c369bf73d ! 2: 9e5400a1ad t7406: simplify by using diff --name-only instead of diff --raw @@ -16,10 +16,10 @@ compare_head ) && - git diff --raw | grep "submodule" && -+ git diff --name-only | grep submodule && ++ git diff --name-only | grep ^submodule$ && git submodule update && - git diff --raw | grep "submodule" && -+ git diff --name-only | grep submodule && ++ git diff --name-only | grep ^submodule$ && (cd submodule && compare_head ) && @@ -28,10 +28,12 @@ compare_head ) && - git diff --raw | grep "submodule" && -+ git diff --name-only | grep submodule && ++ git diff --name-only | grep ^submodule$ && git submodule update --checkout && -- test_must_fail git diff --raw \| grep "submodule" && -+ test_must_fail git diff --name-only \| grep submodule && +- git diff --raw >out && +- ! grep " submodule" out && ++ git diff --name-only >out && ++ ! grep ^submodule$ out && (cd submodule && test_must_fail compare_head ) && 2: ba50d6b0f3 ! 3: 4e8cdf60f4 t7406: avoid having git commands upstream of a pipe @@ -26,13 +26,13 @@ git checkout master && compare_head ) && -- git diff --name-only | grep submodule && +- git diff --name-only | grep ^submodule$ && + git diff --name-only >out && -+ grep submodule out && ++ grep ^submodule$ out && git submodule update && -- git diff --name-only | grep submodule && +- git diff --name-only | grep ^submodule$ && + git diff --name-only >out && -+ grep submodule out && ++ grep ^submodule$ out && (cd submodule && compare_head ) && @@ -40,12 +40,12 @@ git checkout master && compare_head ) && -- git diff --name-only | grep submodule && +- git diff --name-only | grep ^submodule$ && + git diff --name-only >out && -+ grep submodule out && ++ grep ^submodule$ out && git submodule update --checkout && -test_must_fail git diff --name-only \| grep submodule && -(cd submodule && +git diff --name-only >out && +! grep ^submodule$ out && @@ H=$(git rev-parse --short HEAD) && git commit -am "pre move" && 3: 42f7b7f225 ! 4: f171cbcc9a t7406: prefer test_* helper functions to test -[feds] @@ -6,13 +6,12 @@ test failures, so use the test_* helper functions from test-lib-functions.sh. -Note: The use of 'test_path_is_file submodule/.git' may look odd, but -it is a file which is populated with a +Also, add test_path_exists() to test-lib-function.sh while at it, so +that we don't need to worry whether submodule/.git is a file or a +directory. It currently is a file with contents of the form gitdir: ../.git/modules/submodule -directive. If, in the future, handling of the submodule is changed and -submodule/.git becomes a directory we can change this to -test_path_is_dir (or perhaps write a test_path_exists helper function -that doesn't care whether the path is a file or a directory). +but it could be changed in the future to be a directory; this test +only really cares that it exists. Signed-off-by: Elijah Newren @@ -34,8 +33,27 @@ git submodule update --init && - test -e submodule/.git && - test_must_fail test -e none/.git -+ test_path_is_file submodule/.git && ++ test_path_exists submodule/.git && + test_path_is_missing none/.git ) ' + +diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh +--- a/t/test-lib-functions.sh