* Re: [PATCH v2] upload-pack: Optionally allow fetching any sha1
From: Junio C Hamano @ 2016-11-13 1:23 UTC (permalink / raw)
To: David Turner; +Cc: git, spearce
In-Reply-To: <1478885028-30357-1-git-send-email-dturner@twosigma.com>
David Turner <dturner@twosigma.com> writes:
> diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
> index 43665ab..8d3db40 100755
It seems that I haven't heard of 43665ab.
> --- a/t/t5551-http-fetch-smart.sh
> +++ b/t/t5551-http-fetch-smart.sh
> @@ -306,6 +306,28 @@ test_expect_success 'test allowreachablesha1inwant with unreachable' '
> test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
> '
Specifically, the above seems to be missing in my tree.
Perhaps you noticed the lack of test for allowReachableSHA1InWant
and added one, but forgot to send it out, while building this patch
on top?
> +uploadpack.allowAnySHA1InWant::
> + Allow `upload-pack` to accept a fetch request that asks for any
> + object at all.
> + Defaults to `false`.
^ permalink raw reply
* Re: [PATCH] t0021, t5615: use $PWD instead of $(pwd) in PATH-like shell variables
From: Junio C Hamano @ 2016-11-13 1:13 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Johannes Sixt, git, Jeff King
In-Reply-To: <alpine.DEB.2.20.1611121237230.3746@virtualbox>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> Thanks. Dscho, does this fix both of these issues to you?
>
> Apparently it does because the CI jobs for `master` and for `next` pass.
OK, thanks for a quick confirmation.
> The one for `pu` still times out, of course.
Earlier you said 'pu' did even not build, but do we know where this
"still times out" comes from? As long as I don't merge anything
prematurely, which I need to be careful about, it shouldn't impact
the upcoming release, but we'd need to figure it out before moving
things forward post release.
Thanks.
^ permalink raw reply
* Re: [PATCH v7 11/17] ref-filter: introduce symref_atom_parser() and refname_atom_parser()
From: Karthik Nayak @ 2016-11-12 20:12 UTC (permalink / raw)
To: Jacob Keller; +Cc: Git mailing list
In-Reply-To: <CA+P7+xrHRMZemNfo3gjJryBRR5b4T8VT3t9_CBE5hJJfv+D3PQ@mail.gmail.com>
On Wed, Nov 9, 2016 at 5:22 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:
>> From: Karthik Nayak <karthik.188@gmail.com>
>
> What's the reasoning for using separate functions here if they are
> exactly identical except for name? Do we intend to add separate
> options for this? I don't really have a problem with separate
> functions here since it helps avoid confusion but they are identical
> otherwise...
>
I see no need to have separate functions. Maybe in the future when we
plan on adding
specific options we could split them. I'll drop the
symref_atom_parser() function and use
refname_atom_parser() both places.
--
Regards,
Karthik Nayak
^ permalink raw reply
* Re: [PATCH v7 08/17] ref-filter: add support for %(upstream:track,nobracket)
From: Karthik Nayak @ 2016-11-12 20:01 UTC (permalink / raw)
To: Jacob Keller; +Cc: Git mailing list
In-Reply-To: <CA+P7+xrm-MMRa9RuaHUGNWJmH8UcYPj3RZCsy1uvVRMuLeP0ZQ@mail.gmail.com>
On Wed, Nov 9, 2016 at 5:15 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
> On Tue, Nov 8, 2016 at 12:12 PM, Karthik Nayak <karthik.188@gmail.com> wrote:
>> From: Karthik Nayak <karthik.188@gmail.com>
>>
>> Add support for %(upstream:track,nobracket) which will print the
>> tracking information without the brackets (i.e. "ahead N, behind M").
>> This is needed when we port branch.c to use ref-filter's printing APIs.
>>
>
> Makes sense. Seems a bit weird that we have the brackets normally
> rather than adding them as an option, but I think this is ok. We don't
> want to change all previous uses in this case.
>
> My only suggestion here would be to add code so that the options die()
> when we use nobracket along with trackshort or without track. This
> ensures that the nobracket option only applies to track mode?
>
Sure, seems reasonable. There's also the fact that even though nobracket
actually only supports the 'track' option. It might make sense to
leave it as it
is, i.e not die() when used with the other options cause, it kinda stays true to
it being used. e.g. %(upstream:nobracket,trackshort) does give 'trackshort'
without brackets, even though that's the default option and only way we print
'trackshort' information, it seems to make sense from the users point of view.
>> Add test and documentation for the same.
>>
>> Mentored-by: Christian Couder <christian.couder@gmail.com>
>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
>> ---
>> Documentation/git-for-each-ref.txt | 8 +++--
>> ref-filter.c | 67 +++++++++++++++++++++++++-------------
>> t/t6300-for-each-ref.sh | 2 ++
>> 3 files changed, 51 insertions(+), 26 deletions(-)
>>
>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>> index fd365eb..3953431 100644
>> --- a/Documentation/git-for-each-ref.txt
>> +++ b/Documentation/git-for-each-ref.txt
>> @@ -118,9 +118,11 @@ upstream::
>> `refname` above. Additionally respects `:track` to show
>> "[ahead N, behind M]" and `:trackshort` to show the terse
>> version: ">" (ahead), "<" (behind), "<>" (ahead and behind),
>> - or "=" (in sync). Has no effect if the ref does not have
>> - tracking information associated with it. `:track` also prints
>> - "[gone]" whenever unknown upstream ref is encountered.
>> + or "=" (in sync). `:track` also prints "[gone]" whenever
>> + unknown upstream ref is encountered. Append `:track,nobracket`
>> + to show tracking information without brackets (i.e "ahead N,
>> + behind M"). Has no effect if the ref does not have tracking
>> + information associated with it.
>>
>
> Ok so my comment on the previous patch is fixed here, the new wording
> makes it much more clear that [gone] is not the same thing as no
> information. So I don't think we should bother changing the previous
> patch in the series. This might want to document that nobracket works
> even without track, even if it doesn't actually do anything? Or make
> the code more strict in that we die() if the values are put together
> that make no sense?
>
Speaking from what I said above, maybe it makes sense to leave it like it is?
>> push::
>> The name of a local ref which represents the `@{push}` location
>> diff --git a/ref-filter.c b/ref-filter.c
>> index 6d51b80..4d7e414 100644
>> --- a/ref-filter.c
>> +++ b/ref-filter.c
>> @@ -46,8 +46,10 @@ static struct used_atom {
>> union {
>> char color[COLOR_MAXLEN];
>> struct align align;
>> - enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
>> - remote_ref;
>> + struct {
>> + enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT } option;
>> + unsigned int nobracket: 1;
>> + } remote_ref;
>> struct {
>> enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;
>> unsigned int nlines;
>> @@ -75,16 +77,33 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)
>>
>> static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
>> {
>> - if (!arg)
>> - atom->u.remote_ref = RR_NORMAL;
>> - else if (!strcmp(arg, "short"))
>> - atom->u.remote_ref = RR_SHORTEN;
>> - else if (!strcmp(arg, "track"))
>> - atom->u.remote_ref = RR_TRACK;
>> - else if (!strcmp(arg, "trackshort"))
>> - atom->u.remote_ref = RR_TRACKSHORT;
>> - else
>> - die(_("unrecognized format: %%(%s)"), atom->name);
>> + struct string_list params = STRING_LIST_INIT_DUP;
>> + int i;
>> +
>> + if (!arg) {
>> + atom->u.remote_ref.option = RR_NORMAL;
>> + return;
>> + }
>> +
>> + atom->u.remote_ref.nobracket = 0;
>> + string_list_split(¶ms, arg, ',', -1);
>> +
>> + for (i = 0; i < params.nr; i++) {
>> + const char *s = params.items[i].string;
>> +
>> + if (!strcmp(s, "short"))
>> + atom->u.remote_ref.option = RR_SHORTEN;
>> + else if (!strcmp(s, "track"))
>
> Should we add die()s here to disallow setting the remote_ref option
> multiple times? Otherwise, we should document that the last one wins?
> Not sure it's really a big deal here, but we could do it to ensure
> consistency for options which don't make sense together?
>
It makes sense to add a small note that the last option wins, I suppose
--
Regards,
Karthik Nayak
^ permalink raw reply
* Re: [PATCH v7 07/17] ref-filter: make %(upstream:track) prints "[gone]" for invalid upstreams
From: Karthik Nayak @ 2016-11-12 18:48 UTC (permalink / raw)
To: Jacob Keller; +Cc: Git mailing list
In-Reply-To: <CA+P7+xoKxGiNJO4g=Nm+KY=5=u8g2edoLOoe5K0ZAPrvoGj-Ug@mail.gmail.com>
On Wed, Nov 9, 2016 at 5:07 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
>> Make changes to t/t6300-for-each-ref.sh and
>> Documentation/git-for-each-ref.txt to reflect this change.
>>
>
> This will change behavior if people were expecting it to remain
> silent, but I think this could be considered a bug.
>
Didn't get you.
>> Mentored-by: Christian Couder <christian.couder@gmail.com>
>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
>> Helped-by : Jacob Keller <jacob.keller@gmail.com>
>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
>> ---
>> Documentation/git-for-each-ref.txt | 3 ++-
>> ref-filter.c | 4 +++-
>> t/t6300-for-each-ref.sh | 2 +-
>> 3 files changed, 6 insertions(+), 3 deletions(-)
>>
>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>> index 92184c4..fd365eb 100644
>> --- a/Documentation/git-for-each-ref.txt
>> +++ b/Documentation/git-for-each-ref.txt
>> @@ -119,7 +119,8 @@ upstream::
>> "[ahead N, behind M]" and `:trackshort` to show the terse
>> version: ">" (ahead), "<" (behind), "<>" (ahead and behind),
>> or "=" (in sync). Has no effect if the ref does not have
>> - tracking information associated with it.
>> + tracking information associated with it. `:track` also prints
>> + "[gone]" whenever unknown upstream ref is encountered.
>>
>
> I think this is poorly worded. If I understand, "has no effect if the
> ref does not have tracking information" so in that case we still print
> nothing, right? but otherwise we print [gone] only when the upstream
> ref no longer actually exists locally? I wonder if there is a better
> wording for this? I don't have one. Any suggestions to avoid confusing
> these two cases?
>
Dropping this, since its taken care of in the next patch.
--
Regards,
Karthik Nayak
^ permalink raw reply
* Re: Feature request - show result of URL rewrites
From: Dennis Kaarsemaker @ 2016-11-12 18:17 UTC (permalink / raw)
To: Git User, git
In-Reply-To: <284668531.626.1478972994238.JavaMail.root@ichabod>
On Sat, 2016-11-12 at 18:49 +0100, Git User wrote:
> Hello
>
> Hopefully this is the right place to submit feature requests - let me
> know if there's somewhere else I should use!
>
> Git lets you rewrite URLs using "url.<base>.insteadOf"
>
> https://stackoverflow.com/a/11383587
> https://git-scm.com/docs/git-config
>
> Can you add a git-config option to show the result of this rewriting
> whenever this occurs, as debugging more complicated rules can be
> difficult/wasn't obvious without Wireshark.
>
> E.g. you could have the option 'url.printRewrites [True/False]' which
> would print the line "Rewrote url 'git://github.com/git/git' to 'http
> s://github.com/git/git'" to terminal/stdout when set to True.
Such a configuration would be superfluous, the GIT_TRACE and
GIT_CURL_VERBOSE environment variables already provide all the
debugging information you need here.
D.
^ permalink raw reply
* Feature request - show result of URL rewrites
From: Git User @ 2016-11-12 17:49 UTC (permalink / raw)
To: git
Hello
Hopefully this is the right place to submit feature requests - let me know if there's somewhere else I should use!
Git lets you rewrite URLs using "url.<base>.insteadOf"
https://stackoverflow.com/a/11383587
https://git-scm.com/docs/git-config
Can you add a git-config option to show the result of this rewriting whenever this occurs, as debugging more complicated rules can be difficult/wasn't obvious without Wireshark.
E.g. you could have the option 'url.printRewrites [True/False]' which would print the line "Rewrote url 'git://github.com/git/git' to 'https://github.com/git/git'" to terminal/stdout when set to True.
Thanks
^ permalink raw reply
* Re: [RFC] Add way to make Git credentials accessible from clean/smudge filter
From: Lars Schneider @ 2016-11-12 13:57 UTC (permalink / raw)
To: Jeff King; +Cc: Dennis Kaarsemaker, Matthieu Moy, git, me
In-Reply-To: <20161111202710.7zpalypsnorqeclq@sigill.intra.peff.net>
> On 11 Nov 2016, at 21:27, Jeff King <peff@peff.net> wrote:
>
> On Fri, Nov 11, 2016 at 09:02:52PM +0100, Dennis Kaarsemaker wrote:
>
>>>> Are you sure about that? If I do:
>>>>
>>>> echo url=https://example.com/repo.git |
>>>> git credential fill
>>>>
>>>> I get prompted for a username and password.
>>>
>>>
>>> Hm.. either I don't understand you or I expressed myself unclear.
>>>
>>> Let's say a user runs:
>>>
>>> $ git clone https://myrepo.git
>>>
>>> If no credential helper is setup, then Git asks the user for credentials.
>>> Afterwards Git starts downloading stuff. At some point Git will run my
>>> smudge filter on some files and in my case the smudge filter needs the
>>> Git credentials. AFAIK, the smudge filter has no way to get the credentials
>>> from Git at this point - not even by invoking "git credential".
>>> Is this correct?
>>
>> I think that's correct, but the same argument goes both ways: unless I
>> use a credential helper, or explicitely give a filter application my
>> credentials, I don't want a helper to be able to get to those
>> credentials. I'd consider that a security bug.
>
> Yeah, agreed. They are logically two separate operations, so I think it
> is a feature that they do not implicitly share credentials.
>
> I think the only place where we implicitly share credentials is when
> serving an HTTP fetch or push requires multiple HTTP requests. And there
> it seems pretty sane to do so.
Agreed. Thanks for your thoughts on this!
- Lars
^ permalink raw reply
* Re: 2.11.0-rc1 will not be tagged for a few days
From: Lars Schneider @ 2016-11-12 13:33 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git
In-Reply-To: <a90fe336-5b62-4278-64e6-e73cb65a02a8@kdbg.org>
> On 11 Nov 2016, at 22:22, Johannes Sixt <j6t@kdbg.org> wrote:
>
> Am 11.11.2016 um 22:07 schrieb Junio C Hamano:
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> OK, then let's have
>>>
>>> filter_git () {
>>> rm -f rot13-filter.log &&
>>> git "$@"
>>> ...
>>>
>>> and call that -rc1.
>>
>> That is, to queue this on top of ls/filter-process and merge it
>> down.
>>
>> -- >8 --
>> Subject: t0021: remove debugging cruft
>>
>> The redirection of the standard error stream to a temporary file is
>> a leftover cruft during debugging. Remove it.
>>
>> Besides, it is reported by folks on the Windows that the test is
>> flaky with this redirection; somebody gets confused and this
>> merely-redirected-to file gets marked as delete-pending by git.exe
>> and makes it finish with a non-zero exit status when "git checkout"
>> finishes. Windows folks may want to figure that one out, but for
>> the purpose of this test, it shouldn't become a show-stopper.
>>
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>
> Nicely summarized! Thank you very much!
>
> I've double-checked again that the redirection is really not used by the tests. The name does occur in a .gitignore, but I guess that is not important for the background filter process. It would be good if Lars could confirm this.
Yesterday, I wasn't sure if silencing stderr was required by the test.
Junio's assessment and solution is spot on.
Thanks a lot,
Lars
>
>> ---
>> t/t0021-conversion.sh | 3 +--
>> 1 file changed, 1 insertion(+), 2 deletions(-)
>>
>> diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
>> index cb72fa49de..5ce2535017 100755
>> --- a/t/t0021-conversion.sh
>> +++ b/t/t0021-conversion.sh
>> @@ -27,8 +27,7 @@ file_size () {
>>
>> filter_git () {
>> rm -f rot13-filter.log &&
>> - git "$@" 2>git-stderr.log &&
>> - rm -f git-stderr.log
>> + git "$@"
>> }
>>
>> # Compare two files and ensure that `clean` and `smudge` respectively are
>>
>
^ permalink raw reply
* Re: [PATCH] t0021, t5615: use $PWD instead of $(pwd) in PATH-like shell variables
From: Johannes Schindelin @ 2016-11-12 11:40 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Johannes Sixt, git, Jeff King
In-Reply-To: <xmqqa8d53ky9.fsf@gitster.mtv.corp.google.com>
Hi Junio,
On Fri, 11 Nov 2016, Junio C Hamano wrote:
> Johannes Sixt <j6t@kdbg.org> writes:
>
> > We have to use $PWD instead of $(pwd) because on Windows the latter
> > would add a C: style path to bash's Unix-style $PATH variable, which
> > becomes confused by the colon after the drive letter. ($PWD is a
> > Unix-style path.)
> >
> > In the case of GIT_ALTERNATE_OBJECT_DIRECTORIES, bash on Windows
> > assembles a Unix-style path list with the colon as separators. It
> > converts the value to a Windows-style path list with the semicolon as
> > path separator when it forwards the variable to git.exe. The same
> > confusion happens when bash's original value is contaminated with
> > Windows style paths.
> >
> > Signed-off-by: Johannes Sixt <j6t@kdbg.org>
> > ---
> > Am 11.11.2016 um 18:11 schrieb Johannes Sixt:
> >> Am 11.11.2016 um 18:06 schrieb Junio C Hamano:
> >>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:
> >>> ...
> >
> > When the MSYS program such as bash invokes a non-MSYS program, it
> > translates the Unix-style paths in arguments and environment variables
> > to Windows stlye. We only have to ensure that we inject only Unix-style
> > paths in these places so as not to confuse the conversion algorithm.
> > Most of the time, we do not have to worry.
> >
> > On the other hand, when we write a path to a file that git.exe consumes
> > or receive a path from git.exe, i.e., when the path travels through
> > stdout and stdin, no automatic translation happens (which is quite
> > understandable), and we have do the translation explicitly. An example
> > for such a case is when we write a .git/info/alternates file via the
> > shell.
> >
> >> A simpler fix is to use $PWD instead of $(pwd). I'll submit a patch in a
> >> moment.
> >
> > Here it is. I had proposed the t0021 part earlier, but it fell through
> > the cracks during the temporary maintainer change.
>
> Thanks. Dscho, does this fix both of these issues to you?
Apparently it does because the CI jobs for `master` and for `next` pass.
The one for `pu` still times out, of course.
Ciao,
Dscho
^ permalink raw reply
* Re: [RFC/PATCH 0/2] git diff <(command1) <(command2)
From: Johannes Schindelin @ 2016-11-12 10:08 UTC (permalink / raw)
To: Jacob Keller; +Cc: Junio C Hamano, Dennis Kaarsemaker, Git mailing list
In-Reply-To: <CA+P7+xrFYj7sepdz1xrEEaqmXQWfRkBz3CsWjYK6OZsZRMLbUA@mail.gmail.com>
Hi,
On Fri, 11 Nov 2016, Jacob Keller wrote:
> On Fri, Nov 11, 2016 at 1:27 PM, Junio C Hamano <gitster@pobox.com> wrote:
> > Dennis Kaarsemaker <dennis@kaarsemaker.net> writes:
> >
> >> No tests or documentation updates yet, and I'm not sure whether
> >> --follow-symlinks in other modes than --no-index should be supported, ignored
> >> (as it is now) or cause an error, but I'm leaning towards the third option.
> >
> > My knee-jerk reaction is:
> >
> > * The --no-index mode should default to your --follow-symlinks
> > behaviour, without any option to turn it on or off.
> >
>
> I agree. We shouldn't have to specify this for no-index.
Ummm. *My* idea of --no-index was for it to behave as similar to the
--index version as possible. For example when comparing directories
containing symlinks. You seem intent on breaking this scenario.
Ciao,
Johannes
^ permalink raw reply
* Re: [PATCH v7 04/17] ref-filter: modify "%(objectname:short)" to take length
From: Karthik Nayak @ 2016-11-12 9:56 UTC (permalink / raw)
To: Jacob Keller; +Cc: Git mailing list
In-Reply-To: <CA+P7+xqXY6XvUm8Kna_GwY4XOSSGPM=-ezZECgkGcbnX_Penmg@mail.gmail.com>
Hello,
On Fri, Nov 11, 2016 at 10:59 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
> On Thu, Nov 10, 2016 at 9:36 AM, Karthik Nayak <karthik.188@gmail.com> wrote:
>> On Wed, Nov 9, 2016 at 4:57 AM, Jacob Keller <jacob.keller@gmail.com> wrote:
>>
>> That does make sense, It would also not error out when we use
>> %(objectname:short=) and
>> not specify the length. Idk, if that's desirable or not. But it does
>> make the code a little more
>> confusing to read at the same time.
>>
>
> I am not sure that would be the case. If you see "objectname:short"
> you trreat this as if they had passed "objectname:short=<default
> abbrev>" but if you see "objectname:short=" you die, no?
>
Sorry, my bad.
On Fri, Nov 11, 2016 at 5:02 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> else if (!strcmp(arg, "short"))
>> - atom->u.objectname = O_SHORT;
>> - else
>> + atom->u.objectname.option = O_SHORT;
>> + else if (skip_prefix(arg, "short=", &arg)) {
>> + atom->u.objectname.option = O_LENGTH;
>> + if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||
>> + atom->u.objectname.length == 0)
>> + die(_("positive value expected objectname:short=%s"), arg);
>> + if (atom->u.objectname.length < MINIMUM_ABBREV)
>> + atom->u.objectname.length = MINIMUM_ABBREV;
>> + } else
>> die(_("unrecognized %%(objectname) argument: %s"), arg);
>> }
>
> Users who want to use the default-abbrev, i.e. the autoscaling one
> introduced recently, must use "short", not "short=-1", with this
> code (especially with the "must be at least MINIMUM_ABBREV" logic),
> but I do not think it is a problem, so I think this is good.
>
I think I'll leave this as it is. If that's okay
--
Regards,
Karthik Nayak
^ permalink raw reply
* Re: [PATCH v7 03/17] ref-filter: implement %(if:equals=<string>) and %(if:notequals=<string>)
From: Karthik Nayak @ 2016-11-12 9:19 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jacob Keller, Git mailing list
In-Reply-To: <xmqqoa1m5335.fsf@gitster.mtv.corp.google.com>
Hello,
On Fri, Nov 11, 2016 at 4:56 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Jacob Keller <jacob.keller@gmail.com> writes:
>
>>> @@ -49,6 +51,10 @@ static struct used_atom {
>>> enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;
>>> unsigned int nlines;
>>> } contents;
>>> + struct {
>>> + const char *if_equals,
>>> + *not_equals;
>>
>>
>> Same here, why do we need both strings here stored separately? Could
>> we instead store which state to check and store the string once? I'm
>> not sure that really buys us any storage.
>
> I am not sure if storage is an issue, but I tend to agree that it
> would be semantically cleaner if this was done as a pair of <what
> operation uses this string constant?, the string constant>, and the
> former would be enum { COMPARE_EQUAL, COMPARE_UNEQUAL}.
>
> You could later enhance the comparison operator more easily with
> such an arrangement (e.g. if-equals-case-insensitively).
That's a rather good point you make there, I think using an enum with a string
constant would be ideal for future extensibility too. Thanks for the
explanation,
I shall change it.
--
Regards,
Karthik Nayak
^ permalink raw reply
* [PATCH] git-worktree.txt: Fix typo "to"/"two", add comma
From: Ben North @ 2016-11-12 8:55 UTC (permalink / raw)
To: git; +Cc: pclouds
Signed-off-by: Ben North <ben@redfrontdoor.org>
---
Documentation/git-worktree.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 0aeb020..e257c19 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -133,7 +133,7 @@ OPTIONS
+
If the last path components in the working tree's path is unique among
working trees, it can be used to identify worktrees. For example if
-you only have to working trees at "/abc/def/ghi" and "/abc/def/ggg",
+you only have two working trees, at "/abc/def/ghi" and "/abc/def/ggg",
then "ghi" or "def/ghi" is enough to point to the former working tree.
DETAILS
--
2.9.3
^ permalink raw reply related
* Re: [RFC/PATCH 0/2] git diff <(command1) <(command2)
From: Jeff King @ 2016-11-12 7:06 UTC (permalink / raw)
To: Dennis Kaarsemaker; +Cc: Junio C Hamano, git
In-Reply-To: <1478931094.12153.3.camel@kaarsemaker.net>
On Sat, Nov 12, 2016 at 07:11:34AM +0100, Dennis Kaarsemaker wrote:
> > * If normal "diff" that follows symlinks by default has an option
> > to disable it, then it is OK to also add --no-follow-symlinks
> > that is only valid in the --no-index mode, so that we can mimick
> > it better (I do not think this is the case, though).
>
> It does not, so no new option.
There is "--no-dereference", but it is not that helpful:
$ echo one >1
$ echo two >2
$ ln -s 1 sl1
$ ln -s 2 sl2
$ diff --no-dereference sl1 sl2
Symbolic links sl1 and sl2 differ
$ diff --no-dereference sl1 2
File sl1 is a symbolic link while file 2 is a regular file
So it does do the thing we are talking about here, but diff's handling
of non-file content is not nearly as useful as git's.
That said, I have no problem with proceeding without it and waiting
until somebody actually shows up who really _wants_ --no-dereference. I
won't be surprised if that never happens.
-Peff
^ permalink raw reply
* Re: [RFC/PATCH 0/2] git diff <(command1) <(command2)
From: Dennis Kaarsemaker @ 2016-11-12 6:11 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqinrt1zcx.fsf@gitster.mtv.corp.google.com>
On Fri, 2016-11-11 at 13:27 -0800, Junio C Hamano wrote:
> Dennis Kaarsemaker <dennis@kaarsemaker.net> writes:
>
> > No tests or documentation updates yet, and I'm not sure whether
> > --follow-symlinks in other modes than --no-index should be supported, ignored
> > (as it is now) or cause an error, but I'm leaning towards the third option.
>
>
> My knee-jerk reaction is:
>
> * The --no-index mode should default to your --follow-symlinks
> behaviour, without any option to turn it on or off.
ok.
> * If normal "diff" that follows symlinks by default has an option
> to disable it, then it is OK to also add --no-follow-symlinks
> that is only valid in the --no-index mode, so that we can mimick
> it better (I do not think this is the case, though).
It does not, so no new option.
> * Other modes should not follow symbolic links ever, no need for a
> new option.
Makes sense.
> In any case, I'd advise you not to reroll this too quickly and
> frequently until the end of this cycle. During a feature freeze, I
> won't take new topics in 'pu' as that would add more things I need
> to worry about, and if you reroll in too quick succession, it will
> become harder to identify the latest set and queue after the
> release.
I'm in no hurry, so I'll sit on this until v2.11 is done.
D.
^ permalink raw reply
* [PATCH 11/11] worktree remove: new command
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-worktree.txt | 21 +++++----
builtin/worktree.c | 78 ++++++++++++++++++++++++++++++++++
contrib/completion/git-completion.bash | 5 ++-
t/t2028-worktree-move.sh | 26 ++++++++++++
4 files changed, 120 insertions(+), 10 deletions(-)
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 81f4fee..df0d551 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -14,6 +14,7 @@ SYNOPSIS
'git worktree lock' [--reason <string>] <worktree>
'git worktree move' <worktree> <new-path>
'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree remove' [--force] <worktree>
'git worktree unlock' <worktree>
DESCRIPTION
@@ -81,6 +82,13 @@ prune::
Prune working tree information in $GIT_DIR/worktrees.
+remove::
+
+Remove a working tree. Only clean working trees (no untracked files
+and no modification in tracked files) can be removed. Unclean working
+trees can be removed with `--force`. The main working tree cannot be
+removed.
+
unlock::
Unlock a working tree, allowing it to be pruned, moved or deleted.
@@ -90,9 +98,10 @@ OPTIONS
-f::
--force::
- By default, `add` refuses to create a new working tree when `<branch>`
- is already checked out by another working tree. This option overrides
- that safeguard.
+ By default, `add` refuses to create a new working tree when
+ `<branch>` is already checked out by another working tree and
+ `remove` refuses to remove an unclean working tree. This option
+ overrides that safeguard.
-b <new-branch>::
-B <new-branch>::
@@ -253,12 +262,6 @@ Multiple checkout in general is still experimental, and the support
for submodules is incomplete. It is NOT recommended to make multiple
checkouts of a superproject.
-git-worktree could provide more automation for tasks currently
-performed manually, such as:
-
-- `remove` to remove a linked working tree and its administrative files (and
- warn if the working tree is dirty)
-
GIT
---
Part of the linkgit:git[1] suite
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 11be345..60a6199 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -17,6 +17,7 @@ static const char * const worktree_usage[] = {
N_("git worktree lock [<options>] <path>"),
N_("git worktree move <worktree> <new-path>"),
N_("git worktree prune [<options>]"),
+ N_("git worktree remove [<options>] <worktree>"),
N_("git worktree unlock <path>"),
NULL
};
@@ -621,6 +622,81 @@ static int move_worktree(int ac, const char **av, const char *prefix)
return update_worktree_location(wt, dst.buf);
}
+static int remove_worktree(int ac, const char **av, const char *prefix)
+{
+ int force = 0;
+ struct option options[] = {
+ OPT_BOOL(0, "force", &force,
+ N_("force removing even if the worktree is dirty")),
+ OPT_END()
+ };
+ struct worktree **worktrees, *wt;
+ struct strbuf sb = STRBUF_INIT;
+ const char *reason;
+ int ret = 0;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac != 1)
+ usage_with_options(worktree_usage, options);
+
+ worktrees = get_worktrees();
+ wt = find_worktree(worktrees, prefix, av[0]);
+ if (!wt)
+ die(_("'%s' is not a working directory"), av[0]);
+ if (is_main_worktree(wt))
+ die(_("'%s' is a main working directory"), av[0]);
+ if ((reason = is_worktree_locked(wt))) {
+ if (*reason)
+ die(_("already locked, reason: %s"), reason);
+ die(_("already locked, no reason"));
+ }
+ if (validate_worktree(wt, 0))
+ return -1;
+
+ if (!force) {
+ struct argv_array child_env = ARGV_ARRAY_INIT;
+ struct child_process cp;
+ char buf[1];
+
+ argv_array_pushf(&child_env, "%s=%s/.git",
+ GIT_DIR_ENVIRONMENT, wt->path);
+ argv_array_pushf(&child_env, "%s=%s",
+ GIT_WORK_TREE_ENVIRONMENT, wt->path);
+ memset(&cp, 0, sizeof(cp));
+ argv_array_pushl(&cp.args, "status", "--porcelain", NULL);
+ cp.env = child_env.argv;
+ cp.git_cmd = 1;
+ cp.dir = wt->path;
+ cp.out = -1;
+ ret = start_command(&cp);
+ if (ret)
+ die_errno(_("failed to run git-status on '%s', code %d"),
+ av[0], ret);
+ ret = xread(cp.out, buf, sizeof(buf));
+ if (ret)
+ die(_("'%s' is dirty, use --force to delete it"), av[0]);
+ close(cp.out);
+ ret = finish_command(&cp);
+ if (ret)
+ die_errno(_("failed to run git-status on '%s', code %d"),
+ av[0], ret);
+ }
+ strbuf_addstr(&sb, wt->path);
+ if (remove_dir_recursively(&sb, 0)) {
+ error_errno(_("failed to delete '%s'"), sb.buf);
+ ret = -1;
+ }
+ strbuf_reset(&sb);
+ strbuf_addstr(&sb, git_common_path("worktrees/%s", wt->id));
+ if (remove_dir_recursively(&sb, 0)) {
+ error_errno(_("failed to delete '%s'"), sb.buf);
+ ret = -1;
+ }
+ strbuf_release(&sb);
+ free_worktrees(worktrees);
+ return ret;
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -645,5 +721,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
return unlock_worktree(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "move"))
return move_worktree(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "remove"))
+ return remove_worktree(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 613e03b..f6855af 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2715,7 +2715,7 @@ _git_whatchanged ()
_git_worktree ()
{
- local subcommands="add list lock move prune unlock"
+ local subcommands="add list lock move prune remove unlock"
local subcommand="$(__git_find_on_cmdline "$subcommands")"
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
@@ -2733,6 +2733,9 @@ _git_worktree ()
prune,--*)
__gitcomp "--dry-run --expire --verbose"
;;
+ remove,--*)
+ __gitcomp "--force"
+ ;;
*)
;;
esac
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 74070bd..084acc6 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -89,4 +89,30 @@ test_expect_success 'move main worktree' '
test_must_fail git worktree move . def
'
+test_expect_success 'remove main worktree' '
+ test_must_fail git worktree remove .
+'
+
+test_expect_success 'remove locked worktree' '
+ git worktree lock destination &&
+ test_must_fail git worktree remove destination &&
+ git worktree unlock destination
+'
+
+test_expect_success 'remove worktree with dirty tracked file' '
+ echo dirty >>destination/init.t &&
+ test_must_fail git worktree remove destination
+'
+
+test_expect_success 'remove worktree with untracked file' '
+ git -C destination checkout init.t &&
+ : >destination/untracked &&
+ test_must_fail git worktree remove destination
+'
+
+test_expect_success 'force remove worktree with untracked file' '
+ git worktree remove --force destination &&
+ test_path_is_missing destination
+'
+
test_done
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 09/11] worktree move: accept destination as directory
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
Similar to "mv a b/", which is actually "mv a b/a", we extract basename
of source worktree and create a directory of the same name at
destination if dst path is a directory.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/worktree.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index c0d4a73..307019c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -539,7 +539,13 @@ static int move_worktree(int ac, const char **av, const char *prefix)
strbuf_addstr(&dst, prefix_filename(prefix,
strlen(prefix),
av[1]));
- if (file_exists(dst.buf))
+ if (is_directory(dst.buf))
+ /*
+ * keep going, dst will be appended after we get the
+ * source's absolute path
+ */
+ ;
+ else if (file_exists(dst.buf))
die(_("target '%s' already exists"), av[1]);
worktrees = get_worktrees();
@@ -556,6 +562,17 @@ static int move_worktree(int ac, const char **av, const char *prefix)
if (validate_worktree(wt, 0))
return -1;
+ if (is_directory(dst.buf)) {
+ const char *sep = find_last_dir_sep(wt->path);
+
+ if (!sep)
+ die(_("could not figure out destination name from '%s'"),
+ wt->path);
+ strbuf_addstr(&dst, sep);
+ if (file_exists(dst.buf))
+ die(_("target '%s' already exists"), dst.buf);
+ }
+
/*
* First try. Atomically move, and probably cheaper, if both
* source and target are on the same file system.
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 08/11] worktree move: new command
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
There are two options to move the main worktree, but both have
complications, so it's not implemented yet. Anyway the options are:
- convert the main worktree to a linked one and move it away, leave the
git repository where it is. The repo essentially becomes bare after
this move.
- move the repository with the main worktree. The tricky part is make
sure all file descriptors to the repository are closed, or it may
fail on Windows.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-worktree.txt | 7 +++-
builtin/worktree.c | 61 ++++++++++++++++++++++++++++++++++
contrib/completion/git-completion.bash | 2 +-
t/t2028-worktree-move.sh | 30 +++++++++++++++++
4 files changed, 98 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 0aeb020..81f4fee 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -12,6 +12,7 @@ SYNOPSIS
'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
'git worktree list' [--porcelain]
'git worktree lock' [--reason <string>] <worktree>
+'git worktree move' <worktree> <new-path>
'git worktree prune' [-n] [-v] [--expire <expire>]
'git worktree unlock' <worktree>
@@ -71,6 +72,11 @@ files from being pruned automatically. This also prevents it from
being moved or deleted. Optionally, specify a reason for the lock
with `--reason`.
+move::
+
+Move a working tree to a new location. Note that the main working tree
+cannot be moved yet.
+
prune::
Prune working tree information in $GIT_DIR/worktrees.
@@ -252,7 +258,6 @@ performed manually, such as:
- `remove` to remove a linked working tree and its administrative files (and
warn if the working tree is dirty)
-- `mv` to move or rename a working tree and update its administrative files
GIT
---
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 5c4854d..c0d4a73 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -15,6 +15,7 @@ static const char * const worktree_usage[] = {
N_("git worktree add [<options>] <path> [<branch>]"),
N_("git worktree list [<options>]"),
N_("git worktree lock [<options>] <path>"),
+ N_("git worktree move <worktree> <new-path>"),
N_("git worktree prune [<options>]"),
N_("git worktree unlock <path>"),
NULL
@@ -522,6 +523,64 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
return ret;
}
+static int move_worktree(int ac, const char **av, const char *prefix)
+{
+ struct option options[] = {
+ OPT_END()
+ };
+ struct worktree **worktrees, *wt;
+ struct strbuf dst = STRBUF_INIT;
+ const char *reason;
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac != 2)
+ usage_with_options(worktree_usage, options);
+
+ strbuf_addstr(&dst, prefix_filename(prefix,
+ strlen(prefix),
+ av[1]));
+ if (file_exists(dst.buf))
+ die(_("target '%s' already exists"), av[1]);
+
+ worktrees = get_worktrees();
+ wt = find_worktree(worktrees, prefix, av[0]);
+ if (!wt)
+ die(_("'%s' is not a working directory"), av[0]);
+ if (is_main_worktree(wt))
+ die(_("'%s' is a main working directory"), av[0]);
+ if ((reason = is_worktree_locked(wt))) {
+ if (*reason)
+ die(_("already locked, reason: %s"), reason);
+ die(_("already locked, no reason"));
+ }
+ if (validate_worktree(wt, 0))
+ return -1;
+
+ /*
+ * First try. Atomically move, and probably cheaper, if both
+ * source and target are on the same file system.
+ */
+ if (rename(wt->path, dst.buf) == -1) {
+ if (errno != EXDEV)
+ die_errno(_("failed to move '%s' to '%s'"),
+ wt->path, dst.buf);
+
+ /* second try.. */
+ if (copy_dir_recursively(wt->path, dst.buf))
+ die(_("failed to copy '%s' to '%s'"),
+ wt->path, dst.buf);
+ else {
+ struct strbuf sb = STRBUF_INIT;
+
+ strbuf_addstr(&sb, wt->path);
+ (void)remove_dir_recursively(&sb, 0);
+ strbuf_release(&sb);
+ }
+ }
+
+ return update_worktree_location(wt, dst.buf);
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -544,5 +603,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
return lock_worktree(ac - 1, av + 1, prefix);
if (!strcmp(av[1], "unlock"))
return unlock_worktree(ac - 1, av + 1, prefix);
+ if (!strcmp(av[1], "move"))
+ return move_worktree(ac - 1, av + 1, prefix);
usage_with_options(worktree_usage, options);
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 21016bf..613e03b 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -2715,7 +2715,7 @@ _git_whatchanged ()
_git_worktree ()
{
- local subcommands="add list lock prune unlock"
+ local subcommands="add list lock move prune unlock"
local subcommand="$(__git_find_on_cmdline "$subcommands")"
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
diff --git a/t/t2028-worktree-move.sh b/t/t2028-worktree-move.sh
index 8298aaf..74070bd 100755
--- a/t/t2028-worktree-move.sh
+++ b/t/t2028-worktree-move.sh
@@ -59,4 +59,34 @@ test_expect_success 'unlock worktree twice' '
test_path_is_missing .git/worktrees/source/locked
'
+test_expect_success 'move non-worktree' '
+ mkdir abc &&
+ test_must_fail git worktree move abc def
+'
+
+test_expect_success 'move locked worktree' '
+ git worktree lock source &&
+ test_must_fail git worktree move source destination &&
+ git worktree unlock source
+'
+
+test_expect_success 'move worktree' '
+ git worktree move source destination &&
+ test_path_is_missing source &&
+ git worktree list --porcelain | grep "^worktree" >actual &&
+ cat <<-EOF >expected &&
+ worktree $TRASH_DIRECTORY
+ worktree $TRASH_DIRECTORY/destination
+ worktree $TRASH_DIRECTORY/elsewhere
+ EOF
+ test_cmp expected actual &&
+ git -C destination log --format=%s >actual2 &&
+ echo init >expected2 &&
+ test_cmp expected2 actual2
+'
+
+test_expect_success 'move main worktree' '
+ test_must_fail git worktree move . def
+'
+
test_done
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 10/11] worktree move: refuse to move worktrees with submodules
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
Submodules contains .git files with relative paths. After a worktree
move, these files need to be updated or they may point to nowhere.
This is a bandage patch to make sure "worktree move" don't break
people's worktrees by accident. When .git file update code is in
place, this validate_no_submodules() could be removed.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/worktree.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 307019c..11be345 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -523,6 +523,27 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
return ret;
}
+static void validate_no_submodules(const struct worktree *wt)
+{
+ struct index_state istate = {0};
+ int i, found_submodules = 0;
+
+ if (read_index_from(&istate, worktree_git_path(wt, "index")) > 0) {
+ for (i = 0; i < istate.cache_nr; i++) {
+ struct cache_entry *ce = istate.cache[i];
+
+ if (S_ISGITLINK(ce->ce_mode)) {
+ found_submodules = 1;
+ break;
+ }
+ }
+ }
+ discard_index(&istate);
+
+ if (found_submodules)
+ die(_("This working tree contains submodules and cannot be moved yet"));
+}
+
static int move_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -562,6 +583,8 @@ static int move_worktree(int ac, const char **av, const char *prefix)
if (validate_worktree(wt, 0))
return -1;
+ validate_no_submodules(wt);
+
if (is_directory(dst.buf)) {
const char *sep = find_last_dir_sep(wt->path);
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 04/11] copy.c: style fix
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
copy.c | 50 +++++++++++++++++++++++++++++---------------------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/copy.c b/copy.c
index 074b609..60c7d8a 100644
--- a/copy.c
+++ b/copy.c
@@ -111,8 +111,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (dest_exists) {
if (!S_ISDIR(dest_stat.st_mode))
return error(_("target '%s' is not a directory"), dest);
- /* race here: user can substitute a symlink between
- * this check and actual creation of files inside dest */
+ /*
+ * race here: user can substitute a symlink between
+ * this check and actual creation of files inside dest
+ */
} else {
/* Create DEST */
mode_t mode;
@@ -130,22 +132,24 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (lstat(dest, &dest_stat) < 0)
return error_errno(_("can't stat '%s'"), dest);
}
- /* remember (dev,inode) of each created dir.
- * NULL: name is not remembered */
+ /*
+ * remember (dev,inode) of each created dir. name is
+ * not remembered
+ */
add_to_ino_dev_hashtable(&dest_stat, NULL);
/* Recursively copy files in SOURCE */
dp = opendir(source);
- if (dp == NULL) {
+ if (!dp) {
retval = -1;
goto preserve_mode_ugid_time;
}
- while ((d = readdir(dp)) != NULL) {
+ while ((d = readdir(dp))) {
char *new_source, *new_dest;
new_source = concat_subpath_file(source, d->d_name);
- if (new_source == NULL)
+ if (!new_source)
continue;
new_dest = concat_path_file(dest, d->d_name);
if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
@@ -155,16 +159,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
}
closedir(dp);
- if (!dest_exists
- && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
- ) {
+ if (!dest_exists &&
+ chmod(dest, source_stat.st_mode & ~saved_umask) < 0) {
error_errno(_("can't preserve permissions of '%s'"), dest);
/* retval = -1; - WRONG! copy *WAS* made */
}
goto preserve_mode_ugid_time;
}
- if (S_ISREG(source_stat.st_mode) ) { /* "cp [-opts] regular_file thing2" */
+ if (S_ISREG(source_stat.st_mode)) { /* "cp [-opts] regular_file thing2" */
int src_fd;
int dst_fd;
mode_t new_mode;
@@ -199,7 +202,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (!S_ISREG(source_stat.st_mode))
new_mode = 0666;
- // POSIX way is a security problem versus (sym)link attacks
+ /* POSIX way is a security problem versus (sym)link attacks */
if (!ENABLE_FEATURE_NON_POSIX_CP) {
dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
} else { /* safe way: */
@@ -226,13 +229,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
retval = error_errno(_("error writing to '%s'"), dest);
/* ...but read size is already checked by bb_copyfd_eof */
close(src_fd);
- /* "cp /dev/something new_file" should not
- * copy mode of /dev/something */
+ /*
+ * "cp /dev/something new_file" should not
+ * copy mode of /dev/something
+ */
if (!S_ISREG(source_stat.st_mode))
return retval;
goto preserve_mode_ugid_time;
}
- dont_cat:
+dont_cat:
/* Source is a symlink or a special file */
/* We are lazy here, a bit lax with races... */
@@ -252,20 +257,23 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
error_errno(_("can't preserve %s of '%s'"), "ownership", dest);
}
- /* _Not_ jumping to preserve_mode_ugid_time:
- * symlinks don't have those */
+ /*
+ * _Not_ jumping to preserve_mode_ugid_time: symlinks
+ * don't have those
+ */
return 0;
}
- if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
- || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
- ) {
+ if (S_ISBLK(source_stat.st_mode) ||
+ S_ISCHR(source_stat.st_mode) ||
+ S_ISSOCK(source_stat.st_mode) ||
+ S_ISFIFO(source_stat.st_mode)) {
if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0)
return error_errno(_("can't create '%s'"), dest);
} else
return error(_("unrecognized file '%s' with mode %x"),
source, source_stat.st_mode);
- preserve_mode_ugid_time:
+preserve_mode_ugid_time:
if (1 /*FILEUTILS_PRESERVE_STATUS*/) {
struct timeval times[2];
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 05/11] copy.c: convert copy_file() to copy_dir_recursively()
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
This finally enables busybox's copy_file() code under a new name
(because "copy_file" is already taken in Git code base). Because this
comes from busybox, POSIXy (or even Linuxy) behavior is expected. More
changes may be needed for Windows support.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
cache.h | 1 +
copy.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 179 insertions(+), 38 deletions(-)
diff --git a/cache.h b/cache.h
index a50a61a..a9a72f8 100644
--- a/cache.h
+++ b/cache.h
@@ -1857,6 +1857,7 @@ extern void fprintf_or_die(FILE *, const char *fmt, ...);
extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode);
extern int copy_file_with_time(const char *dst, const char *src, int mode);
+extern int copy_dir_recursively(const char *source, const char *dest);
extern void write_or_die(int fd, const void *buf, size_t count);
extern void fsync_or_die(int fd, const char *);
diff --git a/copy.c b/copy.c
index 60c7d8a..8471f7f 100644
--- a/copy.c
+++ b/copy.c
@@ -1,4 +1,6 @@
#include "cache.h"
+#include "dir.h"
+#include "hashmap.h"
int copy_fd(int ifd, int ofd)
{
@@ -66,21 +68,126 @@ int copy_file_with_time(const char *dst, const char *src, int mode)
return status;
}
-#if 0
-/* Return:
- * -1 error, copy not made
- * 0 copy is made or user answered "no" in interactive mode
- * (failures to preserve mode/owner/times are not reported in exit code)
+struct inode_key {
+ struct hashmap_entry entry;
+ ino_t ino;
+ dev_t dev;
+ /*
+ * Reportedly, on cramfs a file and a dir can have same ino.
+ * Need to also remember "file/dir" bit:
+ */
+ char isdir; /* bool */
+};
+
+struct inode_value {
+ struct inode_key key;
+ char name[FLEX_ARRAY];
+};
+
+#define HASH_SIZE 311u /* Should be prime */
+static inline unsigned hash_inode(ino_t i)
+{
+ return i % HASH_SIZE;
+}
+
+static int inode_cmp(const void *entry, const void *entry_or_key,
+ const void *keydata)
+{
+ const struct inode_value *inode = entry;
+ const struct inode_key *key = entry_or_key;
+
+ return !(inode->key.ino == key->ino &&
+ inode->key.dev == key->dev &&
+ inode->key.isdir == key->isdir);
+}
+
+static const char *is_in_ino_dev_hashtable(const struct hashmap *map,
+ const struct stat *st)
+{
+ struct inode_key key;
+ struct inode_value *value;
+
+ key.entry.hash = hash_inode(st->st_ino);
+ key.ino = st->st_ino;
+ key.dev = st->st_dev;
+ key.isdir = !!S_ISDIR(st->st_mode);
+ value = hashmap_get(map, &key, NULL);
+ return value ? value->name : NULL;
+}
+
+static void add_to_ino_dev_hashtable(struct hashmap *map,
+ const struct stat *st,
+ const char *path)
+{
+ struct inode_value *v;
+ int len = strlen(path);
+
+ v = xmalloc(offsetof(struct inode_value, name) + len + 1);
+ v->key.entry.hash = hash_inode(st->st_ino);
+ v->key.ino = st->st_ino;
+ v->key.dev = st->st_dev;
+ v->key.isdir = !!S_ISDIR(st->st_mode);
+ memcpy(v->name, path, len + 1);
+ hashmap_add(map, v);
+}
+
+/*
+ * Find out if the last character of a string matches the one given.
+ * Don't underrun the buffer if the string length is 0.
*/
-int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
+static inline char *last_char_is(const char *s, int c)
+{
+ if (s && *s) {
+ size_t sz = strlen(s) - 1;
+ s += sz;
+ if ( (unsigned char)*s == c)
+ return (char*)s;
+ }
+ return NULL;
+}
+
+static inline char *concat_path_file(const char *path, const char *filename)
+{
+ struct strbuf sb = STRBUF_INIT;
+ char *lc;
+
+ if (!path)
+ path = "";
+ lc = last_char_is(path, '/');
+ while (*filename == '/')
+ filename++;
+ strbuf_addf(&sb, "%s%s%s", path, (lc==NULL ? "/" : ""), filename);
+ return strbuf_detach(&sb, NULL);
+}
+
+static char *concat_subpath_file(const char *path, const char *f)
+{
+ if (f && is_dot_or_dotdot(f))
+ return NULL;
+ return concat_path_file(path, f);
+}
+
+static int do_unlink(const char *dest)
+{
+ int e = errno;
+
+ if (unlink(dest) < 0) {
+ errno = e; /* do not use errno from unlink */
+ return error_errno(_("can't create '%s'"), dest);
+ }
+ return 0;
+}
+
+static int copy_dir_1(struct hashmap *inode_map,
+ const char *source,
+ const char *dest)
{
/* This is a recursive function, try to minimize stack usage */
- /* NB: each struct stat is ~100 bytes */
struct stat source_stat;
struct stat dest_stat;
- smallint retval = 0;
- smallint dest_exists = 0;
- smallint ovr;
+ int retval = 0;
+ int dest_exists = 0;
+ int ovr;
if (lstat(source, &source_stat) < 0)
return error_errno(_("can't stat '%s'"), source);
@@ -102,7 +209,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
mode_t saved_umask = 0;
/* Did we ever create source ourself before? */
- tp = is_in_ino_dev_hashtable(&source_stat);
+ tp = is_in_ino_dev_hashtable(inode_map, &source_stat);
if (tp)
/* We did! it's a recursion! man the lifeboats... */
return error(_("recursion detected, omitting directory '%s'"),
@@ -132,11 +239,12 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (lstat(dest, &dest_stat) < 0)
return error_errno(_("can't stat '%s'"), dest);
}
+
/*
* remember (dev,inode) of each created dir. name is
* not remembered
*/
- add_to_ino_dev_hashtable(&dest_stat, NULL);
+ add_to_ino_dev_hashtable(inode_map, &dest_stat, "");
/* Recursively copy files in SOURCE */
dp = opendir(source);
@@ -152,7 +260,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (!new_source)
continue;
new_dest = concat_path_file(dest, d->d_name);
- if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
+ if (copy_dir_1(inode_map, new_source, new_dest) < 0)
retval = -1;
free(new_source);
free(new_dest);
@@ -177,53 +285,57 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
goto dont_cat;
}
- if (ENABLE_FEATURE_PRESERVE_HARDLINKS) {
+ if (1 /*ENABLE_FEATURE_PRESERVE_HARDLINKS*/) {
const char *link_target;
- link_target = is_in_ino_dev_hashtable(&source_stat);
+ link_target = is_in_ino_dev_hashtable(inode_map, &source_stat);
if (link_target) {
if (link(link_target, dest) < 0) {
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0)
+ ovr = do_unlink(dest);
+ if (ovr < 0)
return ovr;
if (link(link_target, dest) < 0)
return error_errno(_("can't create link '%s'"), dest);
}
return 0;
}
- add_to_ino_dev_hashtable(&source_stat, dest);
+ add_to_ino_dev_hashtable(inode_map, &source_stat, dest);
}
- src_fd = open_or_warn(source, O_RDONLY);
+ src_fd = open(source, O_RDONLY);
if (src_fd < 0)
- return -1;
+ return error_errno(_("can't open '%s'"), source);
/* Do not try to open with weird mode fields */
new_mode = source_stat.st_mode;
if (!S_ISREG(source_stat.st_mode))
new_mode = 0666;
- /* POSIX way is a security problem versus (sym)link attacks */
- if (!ENABLE_FEATURE_NON_POSIX_CP) {
- dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
- } else { /* safe way: */
- dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
- }
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
if (dst_fd == -1) {
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0) {
+ ovr = do_unlink(dest);
+ if (ovr < 0) {
close(src_fd);
return ovr;
}
/* It shouldn't exist. If it exists, do not open (symlink attack?) */
- dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
if (dst_fd < 0) {
close(src_fd);
- return -1;
+ return error_errno(_("can't open '%s'"), dest);
}
}
- if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+ switch (copy_fd(src_fd, dst_fd)) {
+ case COPY_READ_ERROR:
+ error(_("copy-fd: read returned %s"), strerror(errno));
retval = -1;
+ break;
+ case COPY_WRITE_ERROR:
+ error(_("copy-fd: write returned %s"), strerror(errno));
+ retval = -1;
+ break;
+ }
+
/* Careful with writing... */
if (close(dst_fd) < 0)
retval = error_errno(_("error writing to '%s'"), dest);
@@ -243,19 +355,28 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
/* We are lazy here, a bit lax with races... */
if (dest_exists) {
errno = EEXIST;
- ovr = ask_and_unlink(dest, flags);
- if (ovr <= 0)
+ ovr = do_unlink(dest);
+ if (ovr < 0)
return ovr;
}
if (S_ISLNK(source_stat.st_mode)) {
- char *lpath = xmalloc_readlink_or_warn(source);
- if (lpath) {
- int r = symlink(lpath, dest);
- free(lpath);
+ struct strbuf lpath = STRBUF_INIT;
+ if (!strbuf_readlink(&lpath, source, 0)) {
+ int r = symlink(lpath.buf, dest);
+ strbuf_release(&lpath);
if (r < 0)
return error_errno(_("can't create symlink '%s'"), dest);
if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
error_errno(_("can't preserve %s of '%s'"), "ownership", dest);
+ } else {
+ /* EINVAL => "file: Invalid argument" => puzzled user */
+ const char *errmsg = _("not a symlink");
+ int err = errno;
+
+ if (err != EINVAL)
+ errmsg = strerror(err);
+ error(_("%s: cannot read link: %s"), source, errmsg);
+ strbuf_release(&lpath);
}
/*
* _Not_ jumping to preserve_mode_ugid_time: symlinks
@@ -293,4 +414,23 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
return retval;
}
-#endif
+
+/*
+ * Return:
+ * -1 error, copy not made
+ * 0 copy is made
+ *
+ * Failures to preserve mode/owner/times are not reported in exit
+ * code. No support for preserving SELinux security context. Symlinks
+ * and hardlinks are preserved.
+ */
+int copy_dir_recursively(const char *source, const char *dest)
+{
+ int ret;
+ struct hashmap inode_map;
+
+ hashmap_init(&inode_map, inode_cmp, 1024);
+ ret = copy_dir_1(&inode_map, source, dest);
+ hashmap_free(&inode_map, 1);
+ return ret;
+}
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 01/11] copy.c: import copy_file() from busybox
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
This is busybox's unmodified copy_file() in libbb/copy_file.c from the
GPL2+ commit f2c043acfcf9dad9fd3d65821b81f89986bbe54e (busybox: fix
uninitialized memory when displaying IPv6 addresses -
2016-01-18). This is a no-op commit. More changes are needed before
this new code can compile.
This will be needed for implementing "git worktree move" where we have
to move a directory recursively. We can implement it from scratch, but
then we will have to deal with corner cases (failure to move, circular
symlinks...). And delegating the task to "/bin/mv" takes a way the
ability to clean things up properly when things fail and we may have
to deal with "mv" differences between platforms.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
copy.c | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 331 insertions(+)
diff --git a/copy.c b/copy.c
index 4de6a11..79623ac 100644
--- a/copy.c
+++ b/copy.c
@@ -65,3 +65,334 @@ int copy_file_with_time(const char *dst, const char *src, int mode)
return copy_times(dst, src);
return status;
}
+
+#if 0
+/* Return:
+ * -1 error, copy not made
+ * 0 copy is made or user answered "no" in interactive mode
+ * (failures to preserve mode/owner/times are not reported in exit code)
+ */
+int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
+{
+ /* This is a recursive function, try to minimize stack usage */
+ /* NB: each struct stat is ~100 bytes */
+ struct stat source_stat;
+ struct stat dest_stat;
+ smallint retval = 0;
+ smallint dest_exists = 0;
+ smallint ovr;
+
+/* Inverse of cp -d ("cp without -d") */
+#define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0))
+
+ if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) {
+ /* This may be a dangling symlink.
+ * Making [sym]links to dangling symlinks works, so... */
+ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK))
+ goto make_links;
+ bb_perror_msg("can't stat '%s'", source);
+ return -1;
+ }
+
+ if (lstat(dest, &dest_stat) < 0) {
+ if (errno != ENOENT) {
+ bb_perror_msg("can't stat '%s'", dest);
+ return -1;
+ }
+ } else {
+ if (source_stat.st_dev == dest_stat.st_dev
+ && source_stat.st_ino == dest_stat.st_ino
+ ) {
+ bb_error_msg("'%s' and '%s' are the same file", source, dest);
+ return -1;
+ }
+ dest_exists = 1;
+ }
+
+#if ENABLE_SELINUX
+ if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) {
+ security_context_t con;
+ if (lgetfilecon(source, &con) >= 0) {
+ if (setfscreatecon(con) < 0) {
+ bb_perror_msg("can't set setfscreatecon %s", con);
+ freecon(con);
+ return -1;
+ }
+ } else if (errno == ENOTSUP || errno == ENODATA) {
+ setfscreatecon_or_die(NULL);
+ } else {
+ bb_perror_msg("can't lgetfilecon %s", source);
+ return -1;
+ }
+ }
+#endif
+
+ if (S_ISDIR(source_stat.st_mode)) {
+ DIR *dp;
+ const char *tp;
+ struct dirent *d;
+ mode_t saved_umask = 0;
+
+ if (!(flags & FILEUTILS_RECUR)) {
+ bb_error_msg("omitting directory '%s'", source);
+ return -1;
+ }
+
+ /* Did we ever create source ourself before? */
+ tp = is_in_ino_dev_hashtable(&source_stat);
+ if (tp) {
+ /* We did! it's a recursion! man the lifeboats... */
+ bb_error_msg("recursion detected, omitting directory '%s'",
+ source);
+ return -1;
+ }
+
+ if (dest_exists) {
+ if (!S_ISDIR(dest_stat.st_mode)) {
+ bb_error_msg("target '%s' is not a directory", dest);
+ return -1;
+ }
+ /* race here: user can substitute a symlink between
+ * this check and actual creation of files inside dest */
+ } else {
+ /* Create DEST */
+ mode_t mode;
+ saved_umask = umask(0);
+
+ mode = source_stat.st_mode;
+ if (!(flags & FILEUTILS_PRESERVE_STATUS))
+ mode = source_stat.st_mode & ~saved_umask;
+ /* Allow owner to access new dir (at least for now) */
+ mode |= S_IRWXU;
+ if (mkdir(dest, mode) < 0) {
+ umask(saved_umask);
+ bb_perror_msg("can't create directory '%s'", dest);
+ return -1;
+ }
+ umask(saved_umask);
+ /* need stat info for add_to_ino_dev_hashtable */
+ if (lstat(dest, &dest_stat) < 0) {
+ bb_perror_msg("can't stat '%s'", dest);
+ return -1;
+ }
+ }
+ /* remember (dev,inode) of each created dir.
+ * NULL: name is not remembered */
+ add_to_ino_dev_hashtable(&dest_stat, NULL);
+
+ /* Recursively copy files in SOURCE */
+ dp = opendir(source);
+ if (dp == NULL) {
+ retval = -1;
+ goto preserve_mode_ugid_time;
+ }
+
+ while ((d = readdir(dp)) != NULL) {
+ char *new_source, *new_dest;
+
+ new_source = concat_subpath_file(source, d->d_name);
+ if (new_source == NULL)
+ continue;
+ new_dest = concat_path_file(dest, d->d_name);
+ if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
+ retval = -1;
+ free(new_source);
+ free(new_dest);
+ }
+ closedir(dp);
+
+ if (!dest_exists
+ && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
+ ) {
+ bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ /* retval = -1; - WRONG! copy *WAS* made */
+ }
+ goto preserve_mode_ugid_time;
+ }
+
+ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
+ int (*lf)(const char *oldpath, const char *newpath);
+ make_links:
+ /* Hmm... maybe
+ * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ?
+ * (but realpath returns NULL on dangling symlinks...) */
+ lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link;
+ if (lf(source, dest) < 0) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ if (lf(source, dest) < 0) {
+ bb_perror_msg("can't create link '%s'", dest);
+ return -1;
+ }
+ }
+ /* _Not_ jumping to preserve_mode_ugid_time:
+ * (sym)links don't have those */
+ return 0;
+ }
+
+ if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */
+ !(flags & FILEUTILS_RECUR)
+ /* "cp [-opts] regular_file thing2" */
+ || S_ISREG(source_stat.st_mode)
+ /* DEREF uses stat, which never returns S_ISLNK() == true.
+ * So the below is never true: */
+ /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
+ ) {
+ int src_fd;
+ int dst_fd;
+ mode_t new_mode;
+
+ if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) {
+ /* "cp -d symlink dst": create a link */
+ goto dont_cat;
+ }
+
+ if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) {
+ const char *link_target;
+ link_target = is_in_ino_dev_hashtable(&source_stat);
+ if (link_target) {
+ if (link(link_target, dest) < 0) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ if (link(link_target, dest) < 0) {
+ bb_perror_msg("can't create link '%s'", dest);
+ return -1;
+ }
+ }
+ return 0;
+ }
+ add_to_ino_dev_hashtable(&source_stat, dest);
+ }
+
+ src_fd = open_or_warn(source, O_RDONLY);
+ if (src_fd < 0)
+ return -1;
+
+ /* Do not try to open with weird mode fields */
+ new_mode = source_stat.st_mode;
+ if (!S_ISREG(source_stat.st_mode))
+ new_mode = 0666;
+
+ // POSIX way is a security problem versus (sym)link attacks
+ if (!ENABLE_FEATURE_NON_POSIX_CP) {
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
+ } else { /* safe way: */
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ }
+ if (dst_fd == -1) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0) {
+ close(src_fd);
+ return ovr;
+ }
+ /* It shouldn't exist. If it exists, do not open (symlink attack?) */
+ dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ if (dst_fd < 0) {
+ close(src_fd);
+ return -1;
+ }
+ }
+
+#if ENABLE_SELINUX
+ if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT))
+ && is_selinux_enabled() > 0
+ ) {
+ security_context_t con;
+ if (getfscreatecon(&con) == -1) {
+ bb_perror_msg("getfscreatecon");
+ return -1;
+ }
+ if (con) {
+ if (setfilecon(dest, con) == -1) {
+ bb_perror_msg("setfilecon:%s,%s", dest, con);
+ freecon(con);
+ return -1;
+ }
+ freecon(con);
+ }
+ }
+#endif
+ if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+ retval = -1;
+ /* Careful with writing... */
+ if (close(dst_fd) < 0) {
+ bb_perror_msg("error writing to '%s'", dest);
+ retval = -1;
+ }
+ /* ...but read size is already checked by bb_copyfd_eof */
+ close(src_fd);
+ /* "cp /dev/something new_file" should not
+ * copy mode of /dev/something */
+ if (!S_ISREG(source_stat.st_mode))
+ return retval;
+ goto preserve_mode_ugid_time;
+ }
+ dont_cat:
+
+ /* Source is a symlink or a special file */
+ /* We are lazy here, a bit lax with races... */
+ if (dest_exists) {
+ errno = EEXIST;
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ }
+ if (S_ISLNK(source_stat.st_mode)) {
+ char *lpath = xmalloc_readlink_or_warn(source);
+ if (lpath) {
+ int r = symlink(lpath, dest);
+ free(lpath);
+ if (r < 0) {
+ bb_perror_msg("can't create symlink '%s'", dest);
+ return -1;
+ }
+ if (flags & FILEUTILS_PRESERVE_STATUS)
+ if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ }
+ /* _Not_ jumping to preserve_mode_ugid_time:
+ * symlinks don't have those */
+ return 0;
+ }
+ if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
+ || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
+ ) {
+ if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
+ bb_perror_msg("can't create '%s'", dest);
+ return -1;
+ }
+ } else {
+ bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode);
+ return -1;
+ }
+
+ preserve_mode_ugid_time:
+
+ if (flags & FILEUTILS_PRESERVE_STATUS
+ /* Cannot happen: */
+ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */
+ ) {
+ struct timeval times[2];
+
+ times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime;
+ times[1].tv_usec = times[0].tv_usec = 0;
+ /* BTW, utimes sets usec-precision time - just FYI */
+ if (utimes(dest, times) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "times", dest);
+ if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
+ source_stat.st_mode &= ~(S_ISUID | S_ISGID);
+ bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ }
+ if (chmod(dest, source_stat.st_mode) < 0)
+ bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ }
+
+ if (flags & FILEUTILS_VERBOSE) {
+ printf("'%s' -> '%s'\n", source, dest);
+ }
+
+ return retval;
+}
+#endif
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 07/11] worktree.c: add update_worktree_location()
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
worktree.c | 21 +++++++++++++++++++++
worktree.h | 6 ++++++
2 files changed, 27 insertions(+)
diff --git a/worktree.c b/worktree.c
index 7e15ec7..db63758 100644
--- a/worktree.c
+++ b/worktree.c
@@ -354,6 +354,27 @@ int validate_worktree(const struct worktree *wt, int quiet)
return 0;
}
+int update_worktree_location(struct worktree *wt, const char *path_)
+{
+ struct strbuf path = STRBUF_INIT;
+ int ret = 0;
+
+ if (is_main_worktree(wt))
+ return 0;
+
+ strbuf_add_absolute_path(&path, path_);
+ if (fspathcmp(wt->path, path.buf)) {
+ write_file(git_common_path("worktrees/%s/gitdir",
+ wt->id),
+ "%s/.git", real_path(path.buf));
+ free(wt->path);
+ wt->path = strbuf_detach(&path, NULL);
+ ret = 0;
+ }
+ strbuf_release(&path);
+ return ret;
+}
+
int is_worktree_being_rebased(const struct worktree *wt,
const char *target)
{
diff --git a/worktree.h b/worktree.h
index e782ae5..0477a3d 100644
--- a/worktree.h
+++ b/worktree.h
@@ -55,6 +55,12 @@ extern const char *is_worktree_locked(struct worktree *wt);
*/
extern int validate_worktree(const struct worktree *wt, int quiet);
+/*
+ * Update worktrees/xxx/gitdir with the new path.
+ */
+extern int update_worktree_location(struct worktree *wt,
+ const char *path_);
+
/*
* Free up the memory for worktree(s)
*/
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
* [PATCH 03/11] copy.c: convert bb_(p)error_msg to error(_errno)
From: Nguyễn Thái Ngọc Duy @ 2016-11-12 2:23 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In-Reply-To: <20161112022337.13317-1-pclouds@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
copy.c | 85 ++++++++++++++++++++++++------------------------------------------
1 file changed, 31 insertions(+), 54 deletions(-)
diff --git a/copy.c b/copy.c
index b7a87f1..074b609 100644
--- a/copy.c
+++ b/copy.c
@@ -82,23 +82,16 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
smallint dest_exists = 0;
smallint ovr;
- if (lstat(source, &source_stat) < 0) {
- bb_perror_msg("can't stat '%s'", source);
- return -1;
- }
+ if (lstat(source, &source_stat) < 0)
+ return error_errno(_("can't stat '%s'"), source);
if (lstat(dest, &dest_stat) < 0) {
- if (errno != ENOENT) {
- bb_perror_msg("can't stat '%s'", dest);
- return -1;
- }
+ if (errno != ENOENT)
+ return error_errno(_("can't stat '%s'"), dest);
} else {
- if (source_stat.st_dev == dest_stat.st_dev
- && source_stat.st_ino == dest_stat.st_ino
- ) {
- bb_error_msg("'%s' and '%s' are the same file", source, dest);
- return -1;
- }
+ if (source_stat.st_dev == dest_stat.st_dev &&
+ source_stat.st_ino == dest_stat.st_ino)
+ return error(_("'%s' and '%s' are the same file"), source, dest);
dest_exists = 1;
}
@@ -110,18 +103,14 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
/* Did we ever create source ourself before? */
tp = is_in_ino_dev_hashtable(&source_stat);
- if (tp) {
+ if (tp)
/* We did! it's a recursion! man the lifeboats... */
- bb_error_msg("recursion detected, omitting directory '%s'",
- source);
- return -1;
- }
+ return error(_("recursion detected, omitting directory '%s'"),
+ source);
if (dest_exists) {
- if (!S_ISDIR(dest_stat.st_mode)) {
- bb_error_msg("target '%s' is not a directory", dest);
- return -1;
- }
+ if (!S_ISDIR(dest_stat.st_mode))
+ return error(_("target '%s' is not a directory"), dest);
/* race here: user can substitute a symlink between
* this check and actual creation of files inside dest */
} else {
@@ -134,15 +123,12 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
mode |= S_IRWXU;
if (mkdir(dest, mode) < 0) {
umask(saved_umask);
- bb_perror_msg("can't create directory '%s'", dest);
- return -1;
+ return error_errno(_("can't create directory '%s'"), dest);
}
umask(saved_umask);
/* need stat info for add_to_ino_dev_hashtable */
- if (lstat(dest, &dest_stat) < 0) {
- bb_perror_msg("can't stat '%s'", dest);
- return -1;
- }
+ if (lstat(dest, &dest_stat) < 0)
+ return error_errno(_("can't stat '%s'"), dest);
}
/* remember (dev,inode) of each created dir.
* NULL: name is not remembered */
@@ -172,7 +158,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (!dest_exists
&& chmod(dest, source_stat.st_mode & ~saved_umask) < 0
) {
- bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ error_errno(_("can't preserve permissions of '%s'"), dest);
/* retval = -1; - WRONG! copy *WAS* made */
}
goto preserve_mode_ugid_time;
@@ -196,10 +182,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
ovr = ask_and_unlink(dest, flags);
if (ovr <= 0)
return ovr;
- if (link(link_target, dest) < 0) {
- bb_perror_msg("can't create link '%s'", dest);
- return -1;
- }
+ if (link(link_target, dest) < 0)
+ return error_errno(_("can't create link '%s'"), dest);
}
return 0;
}
@@ -238,10 +222,8 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (bb_copyfd_eof(src_fd, dst_fd) == -1)
retval = -1;
/* Careful with writing... */
- if (close(dst_fd) < 0) {
- bb_perror_msg("error writing to '%s'", dest);
- retval = -1;
- }
+ if (close(dst_fd) < 0)
+ retval = error_errno(_("error writing to '%s'"), dest);
/* ...but read size is already checked by bb_copyfd_eof */
close(src_fd);
/* "cp /dev/something new_file" should not
@@ -265,12 +247,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (lpath) {
int r = symlink(lpath, dest);
free(lpath);
- if (r < 0) {
- bb_perror_msg("can't create symlink '%s'", dest);
- return -1;
- }
+ if (r < 0)
+ return error_errno(_("can't create symlink '%s'"), dest);
if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ error_errno(_("can't preserve %s of '%s'"), "ownership", dest);
}
/* _Not_ jumping to preserve_mode_ugid_time:
* symlinks don't have those */
@@ -279,14 +259,11 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
|| S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
) {
- if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
- bb_perror_msg("can't create '%s'", dest);
- return -1;
- }
- } else {
- bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode);
- return -1;
- }
+ if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0)
+ return error_errno(_("can't create '%s'"), dest);
+ } else
+ return error(_("unrecognized file '%s' with mode %x"),
+ source, source_stat.st_mode);
preserve_mode_ugid_time:
@@ -297,13 +274,13 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
times[1].tv_usec = times[0].tv_usec = 0;
/* BTW, utimes sets usec-precision time - just FYI */
if (utimes(dest, times) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "times", dest);
+ error_errno(_("can't preserve %s of '%s'"), "times", dest);
if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
source_stat.st_mode &= ~(S_ISUID | S_ISGID);
- bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+ error_errno(_("can't preserve %s of '%s'"), "ownership", dest);
}
if (chmod(dest, source_stat.st_mode) < 0)
- bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+ error_errno(_("can't preserve %s of '%s'"), "permissions", dest);
}
return retval;
--
2.8.2.524.g6ff3d78
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox