Git development
 help / color / mirror / Atom feed
* Re: [ANNOUNCE] git-test: run automated tests against a range of Git commits
From: Jeff King @ 2017-01-07  7:18 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: git discussion list
In-Reply-To: <1341c01a-aca7-699c-c53a-28d048614bfe@alum.mit.edu>

On Fri, Jan 06, 2017 at 04:52:16PM +0100, Michael Haggerty wrote:

> I just released ⁠⁠⁠⁠`git test⁠⁠⁠⁠`, a script for running automated
> tests across a range of Git commits and keeping track of the results in
> git notes:
> 
>     https://github.com/mhagger/git-test
> 
> This is a script that I've been using in one form or another for years
> and I find it really handy [1].

Neat. I usually "git rebase -x 'make -j8 test' @{u}" after finishing a
topic to make sure the intermediate steps are good. But it would be neat
to have this running continuously in the background to alert me to
problems sooner (and the key thing there is that it remembers
already-run tests, so it should be safe to basically for new commits
every 10 seconds or so).

I did hit a few interesting cases trying out "git test". So here's a
narrative, and you can pick out where there may be room for improvement
in the tool, and where I'm just being dumb. :)

I tried it out first on a topic I finished earlier today, which has 3
commits. So I did:

  $ git test add 'make -j8 test'
  $ git test range @{u}..HEAD

It barfed on the first commit, because the script expects "git co" to
work, but I don't have that alias. No big deal (and I already submitted
a PR to fix it).

So then I reinvoked it like:

  $ git test range @{u}..HEAD

and it actually ran some tests. Yay.

And then of course I wanted to prove to myself how cool the notes
feature is, so I ran it again. It didn't run any tests this time. Yay
again. But there were a few surprises:

  $ git test range @{u}..HEAD
  setup_test default
  Using test default; command: make -j8 test
  Old status: bad
  Tree 9fcdbd5c78^{tree} is already known to be bad!
  Old status: good
  Tree c22f4f6624^{tree} is already known to be good.
  Old status: good
  Tree 19e2e62e5e^{tree} is already known to be good.
  Already on 'jk/wait-for-child-cleanup'
  Your branch is ahead of 'origin/master' by 3 commits.

  ALL TESTS SUCCESSFUL

My initial run with "git co" had left the first commit marked as "bad".
That's not _too_ surprising, since it did indeed fail. I think it's
probably a bug to record a failure note, though, if checking out fails.
It's not necessarily an immutable property of the tree. In my case, it
was obviously dependent on a change in the git-test code, but that's
hopefully a fairly uncommon occurrence. But there are other reasons for
git-checkout to complain. For instance, imagine topic "foo" creates file
"foo.c". If I do:

  $ echo content >foo.c
  $ git test foo@{u}..foo

then checkout will complain about overwriting the untracked "foo.c".
It's reasonable to abort the operation there, but probably not to write
a permanent failure note. The problem wasn't in "foo", but in my local
tree.

The second thing that surprised me was "ALL TESTS SUCCESSFUL", when
clearly one of them was known-bad. :)

So at this point I knew I needed to re-run the test. Looks like there's
a "--force" option. Let's try it. There's no need to re-run the other
two, so let's just give it one commit:

  $ git test range -f HEAD~2
  ...
  Object 95649d6cf9ec68f05d1dc57ec1b989b8d263a7ae^{tree} has no note
  Object e1970ce43abfbf625bce68516857e910748e5965^{tree} has no note
  Object 368f99d57e8ed17243f2e164431449d48bfca2fb^{tree} has no note
  Object ceede59ea90cebad52ba9c8263fef3fb6ef17593^{tree} has no note
  Object dfe070511c652f2b8e1bf6540f238c9ca9ba41d3^{tree} has no note
  Object 902d960b382a0cd424618ff4e1316da40e4be2f6^{tree} has no note
  ...

This started spewing out many lines like the one above, until I hit ^C.
Yikes!

Thinking something was wrong with the "-f" option, I tried it without:

  $ git test range -f HEAD~2
  ...
  commit e83c5163316f89bfbde7d9ab23ca2e25604af290 (origin/initial)
  Author: Linus Torvalds <torvalds@ppc970.osdl.org>
  Date:   Thu Apr 7 15:13:13 2005 -0700

      Initial revision of "git", the information manager from hell

Oops. Now I see the problem. I was expecting the arguments to be
rebase-like. But they're really rev-list like. So in both cases it
wanted to test all the way up from the root commits to HEAD~2.  The "-f"
version never got to testing a commit because it was so busy trying to
delete the old notes.

I see the symmetry and simplicity in allowing the user to specify a full
range. But it also seems like it's easy to make a mistake that's going
to want to test a lot of commits. I wonder if it should complain when
there's no lower bound to the commit range. Or alternatively, if there's
a single positive reference, treat it as a lower bound, with HEAD as the
upper bound (which is vaguely rebase-like).

A few other observations about the note deletion:

  - The "has no note" message should perhaps be suppressed. We're just
    trying to overwrite the value if there is one (alternatively,
    instead of removing it, just overwrite it, so the old note stays
    until we get a result one way or the other).

  - It was sufficiently slow that it looks like we invoke "git notes
    remove" once per commit. It would be a lot more efficient to batch
    them (not just in terms of process startup, but because you're going
    to write a _ton_ of intermediate notes trees).

    Of course none of that matters if you don't do something stupid like
    trying to "git test" 45,000 commits. :)

So OK. Now I know what's going on. And I can get what I want with:

  $ git test range -f HEAD~3..HEAD~2

which works, and now all of my tests are correctly marked. Of course
that's a lot to type. It would be easier as:

  $ git test range -f -1 HEAD~2
  usage: git-test [-h] {add,range,help} ...
  git-test: error: unrecognized arguments: HEAD~2

but the tool doesn't seem to like passing through the rev-list argument.

It would be even easier if I could just repeat my range and only re-test
the "bad" commits. It was then that I decided to actually read the rest
of "git test help range" and see that you already wrote such an option,
cleverly hidden under the name "--retest".

And one final nit. I notice there is also a "--keep-going" option. Which
made me surprised that we bothered to test HEAD~1 and HEAD, when we knew
that HEAD~2 was bogus. I suspect this is related to the "ALL TESTS
SUCCESSFUL" issue. Presumably cached test results are not treated as
"bad" in general, which seems funny to me.

So those were all little cosmetic things. The other big thing I wanted
to see was what it's like to fix a bug deep in a topic. So I used "git
rebase -i" to inset a compile error into the first commit of my 3-patch
series. And then I tested it:

  $ git test add -t compile 'make -j8'
  $ git test range -t compile HEAD~3..

As predicted, it stopped at the first commit and told me it was buggy.
But I'm dumped onto a detached HEAD, and I'm on my own to actually get
the working tree to a state where I can test and fix on my actual
branch.

That's a nice outcome of the "git rebase -x make" approach. When you hit
a bug, you can fix it, "git commit --amend", and "git rebase
--continue". The downside is that it takes ownership of the branch while
you're doing it. So the whole concept of "run this in the background
with a separate worktree" breaks down completely.

I think it should be possible to script the next steps, though.
Something like like "git test fix foo", which would:

  - expand the range of foo@{u}..foo to get the list of commits

  - see which ones were marked as broken

  - kick off an interactive rebase, but override GIT_EDITOR to mark any
    broken ones as "edit" instead of "pick"

That lets you separate the act of testing from the act of fixing. You
can let the tester run continuously in the background, and only stop to
fix when you're at an appropriate point in your work.

Anyway. Seems like a neat tool. I may play around with it and see if I
can fit it into my workflow.

-Peff

^ permalink raw reply

* Re: [PATCH v2 4/4] mergetool: fix running in subdir when rerere enabled
From: Richard Hansen @ 2017-01-07  2:16 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, davvid, sbeller, simon
In-Reply-To: <199807ae-844c-57cd-28cf-2c10b3aee7a9@kdbg.org>

[-- Attachment #1: Type: text/plain, Size: 2562 bytes --]

On 2017-01-06 04:42, Johannes Sixt wrote:
> Am 06.01.2017 um 02:09 schrieb Richard Hansen:
>> If rerere is enabled and no pathnames are given, run cd_to_toplevel
>> before running 'git diff --name-only' so that 'git diff --name-only'
>> sees the pathnames emitted by 'git rerere remaining'.
>>
>> Also run cd_to_toplevel before running 'git rerere remaining' in case
>> 'git rerere remaining' is ever changed to print pathnames relative to
>> the current directory rather than to $GIT_WORK_TREE.
>>
>> This fixes a regression introduced in
>> 57937f70a09c12ef484c290865dac4066d207c9c (v2.11.0).
>>
>> Signed-off-by: Richard Hansen <hansenr@google.com>
>> ---
>>  git-mergetool.sh     | 1 +
>>  t/t7610-mergetool.sh | 2 +-
>>  2 files changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/git-mergetool.sh b/git-mergetool.sh
>> index e52b4e4f2..67ea0d6db 100755
>> --- a/git-mergetool.sh
>> +++ b/git-mergetool.sh
>> @@ -456,6 +456,7 @@ main () {
>>
>>      if test $# -eq 0 && test -e "$GIT_DIR/MERGE_RR"
>>      then
>> +        cd_to_toplevel
>>          set -- $(git rerere remaining)
>>          if test $# -eq 0
>>          then
>
> This cannot be a complete solution. Why do we have another
> cd_to_toplevel later, after `git diff --name-only -- "$@"`?

The arguments passed to 'git diff' (including the -O argument) are all 
interpreted as relative to the current working directory, yet 'git diff 
--name-only' outputs pathnames that are relative to the top-level 
directory.  Thus:

   * cd_to_toplevel MUST NOT be run before that 'git diff' command
     unless all pathnames relative to $PWD are converted to absolute (or
     relative to the top-level directory), and
   * cd_to_toplevel MUST be run after 'git diff' so that $files is
     interpreted correctly.

And now I realize that my change breaks -O<foo> if <foo> is relative to 
$PWD.  Grr.  Too bad we don't have tests for running mergetool 
-O<relative-path> from a subdirectory.

>
> Maybe it is necessary to revert back to the flow control that we had
> before 57937f70a09c ("mergetool: honor diff.orderFile", 2016-10-07)? It
> did not have `test $# -eq 0` and `test -e "$GIT_DIR/MERGE_RR"` in a
> single condition.

Reverting to the previous control flow won't work unless the -O pathname 
is converted to absolute (or relative to the top-level directory).  But 
I'll have to do that anyway.  Blech.

Do we already have a helper shell function somewhere that converts a 
pathname to absolute?  Thanks to symlinks it's trickier than one might 
expect.

Thanks,
Richard


>
> -- Hannes

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4845 bytes --]

^ permalink raw reply

* Re: [PATCH v2 2/4] t7610: make tests more independent and debuggable
From: Richard Hansen @ 2017-01-07  1:53 UTC (permalink / raw)
  To: Stefan Beller
  Cc: git@vger.kernel.org, David Aguilar, Johannes Sixt, Simon Ruderich
In-Reply-To: <CAGZ79kbRee+3MbAHCSFB0QqGMMF5bcZMiEHV-coRh87vFfq0Ag@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3039 bytes --]

On 2017-01-05 20:31, Stefan Beller wrote:
> On Thu, Jan 5, 2017 at 5:09 PM, Richard Hansen <hansenr@google.com> wrote:
>> Reduce how much a test can interfere other tests:
>
> A bullet point list as an unordered list often indicates that you're
> doing multiple
> things at once, possibly unrelated, so they could go into different patches. ;)

OK, I'll split it up.

>
> While telling you to make even more commits: you may also want to make
> a patch with an entry to the .mailmap file (assuming you're the same
> Richard Hansen that contributed from rhansen@bbn.com);
> Welcome to Google!

Good idea, thanks!

>
>>
>>   * Move setup code that multiple tests depend on to the 'setup' test
>>     case.
>>   * Run 'git reset --hard' after every test (pass or fail) to clean up
>>     in case the test fails and leaves the repository in a strange
>>     state.
>>   * If the repository must be in a particular state (beyond what is
>>     already done by the 'setup' test case) before the test can run,
>>     make the necessary repository changes in the test script even if
>>     it means duplicating some lines of code from the previous test
>>     case.
>>   * Never assume that a particular commit is checked out.
>>   * Always work on a test-specific branch when the test might create a
>>     commit.  This is not always necessary for correctness, but it
>>     improves debuggability by ensuring a commit created by test #N
>>     shows up on the testN branch, not the branch for test #N-1.
>
>
>
>
>> @@ -112,6 +146,7 @@ test_expect_success 'custom mergetool' '
>>  '
>>
>>  test_expect_success 'mergetool crlf' '
>> +       test_when_finished "git reset --hard" &&
>>         test_config core.autocrlf true &&
>>         git checkout -b test$test_count branch1 &&
>>         test_must_fail git merge master >/dev/null 2>&1 &&
>> @@ -129,11 +164,11 @@ test_expect_success 'mergetool crlf' '
>>         git submodule update -N &&
>>         test "$(cat submod/bar)" = "master submodule" &&
>>         git commit -m "branch1 resolved with mergetool - autocrlf" &&
>
>> -       test_config core.autocrlf false &&
>> -       git reset --hard
>> +       test_config core.autocrlf false
>>  '
>
> This is the nit that led me to writing this email in the first place:
> test_config is a function that sets a configuration for a single test only,
> so it makes no sense as the last statement of a test. (In its implementation
> it un-configures with test_when_finished)
>
> So I think we do not want to add it back, but rather remove this
> test_config statement.

OK, will do.

>
> But to do this we need to actually be careful with the order of the newly
> added test_when_finished "git reset --hard" and  test_config core.autocrlf true,
> which uses test_when_finished internally.

Ah, yes.  Tricky.  I'll add a comment.

>
> The order seems correct to me, as the reset would be executed after the
> "test_config core.autocrlf true" is un-configured.

Agreed; test_when_finished is LIFO (though the order is not documented).

-Richard

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4845 bytes --]

^ permalink raw reply

* Re: [PATCH 4/5] unpack-trees: factor file removal out of check_updates
From: Jeff King @ 2017-01-07  1:36 UTC (permalink / raw)
  To: Stefan Beller; +Cc: gitster, l.s.r, git
In-Reply-To: <20170106210330.31761-5-sbeller@google.com>

On Fri, Jan 06, 2017 at 01:03:29PM -0800, Stefan Beller wrote:

> +static int remove_workingtree_files(struct unpack_trees_options *o,
> +				    struct progress *progress)
> +{
> +	int i;
> +	unsigned cnt = 0;
> +	struct index_state *index = &o->result;
> +
> +	for (i = 0; i < index->cache_nr; i++) {
> +		const struct cache_entry *ce = index->cache[i];
> +
> +		if (ce->ce_flags & CE_WT_REMOVE) {
> +			display_progress(progress, ++cnt);
> +			if (o->update && !o->dry_run)
> +				unlink_entry(ce);
> +		}
> +	}
> +
> +	return cnt;
> +}

"cnt" is unsigned here, as it is in the caller. Should the return value
match?

-Peff

^ permalink raw reply

* Re: [PATCH] branch_get_push: do not segfault when HEAD is detached
From: Jeff King @ 2017-01-07  1:31 UTC (permalink / raw)
  To: Kyle Meyer; +Cc: Johannes Schindelin, git
In-Reply-To: <87bmvjll8m.fsf@kyleam.com>

On Fri, Jan 06, 2017 at 08:19:53PM -0500, Kyle Meyer wrote:

> > The other option is just "git checkout --detach", which is also used in
> > the test suite. I tend to prefer it because it's a little more obvious
> > to a reader.
> 
> True, that does seem clearer.  Seems I should've waited a bit before
> sending out v2.

I think it's OK either way. Junio can also mark it up while applying,
too, if he has a preference.

-Peff

^ permalink raw reply

* [PATCH 3/3] execv_dashed_external: wait for child on signal death
From: Jeff King @ 2017-01-07  1:22 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <20170107011445.3e4fv6vdtimrwhgv@sigill.intra.peff.net>

When you hit ^C to interrupt a git command going to a pager,
this usually leaves the pager running. But when a dashed
external is in use, the pager ends up in a funny state and
quits (but only after eating one more character from the
terminal!). This fixes it.

Explaining the reason will require a little background.

When git runs a pager, it's important for the git process to
hang around and wait for the pager to finish, even though it
has no more data to feed it. This is because git spawns the
pager as a child, and thus the git process is the session
leader on the terminal. After it dies, the pager will finish
its current read from the terminal (eating the one
character), and then get EIO trying to read again.

When you hit ^C, that sends SIGINT to git and to the pager,
and it's a similar situation.  The pager ignores it, but the
git process needs to hang around until the pager is done. We
addressed that long ago in a3da882120 (pager: do
wait_for_pager on signal death, 2009-01-22).

But when you have a dashed external (or an alias pointing to
a builtin, which will re-exec git for the builtin), there's
an extra process in the mix. For instance, running:

  git -c alias.l=log log

will end up with a process tree like:

  git (parent)
    \
     git-log (child)
      \
       less (pager)

If you hit ^C, SIGINT goes to all of them. The pager ignores
it, and the child git process will end up in wait_for_pager().
But the parent git process will die, and the usual EIO
trouble happens.

So we really want the parent git process to wait_for_pager(),
but of course it doesn't know anything about the pager at
all, since it was started by the child.  However, we can
have it wait on the git-log child, which in turn is waiting
on the pager. And that's what this patch does.

There are a few design decisions here worth explaining:

  1. The new feature is attached to run-command's
     clean_on_exit feature. Partly this is convenience,
     since that feature already has a signal handler that
     deals with child cleanup.

     But it's also a meaningful connection. The main reason
     that dashed externals use clean_on_exit is to bind the
     two processes together. If somebody kills the parent
     with a signal, we propagate that to the child (in this
     instance with SIGINT, we do propagate but it doesn't
     matter because the original signal went to the whole
     process group). Likewise, we do not want the parent
     to go away until the child has done so.

     In a traditional Unix world, we'd probably accomplish
     this binding by just having the parent execve() the
     child directly. But since that doesn't work on Windows,
     everything goes through run_command's more spawn-like
     interface.

  2. We do _not_ automatically waitpid() on any
     clean_on_exit children. For dashed externals this makes
     sense; we know that the parent is doing nothing but
     waiting for the child to exit anyway. But with other
     children, it's possible that the child, after getting
     the signal, could be waiting on the parent to do
     something (like closing a descriptor). If we were to
     wait on such a child, we'd end up in a deadlock. So
     this errs on the side of caution, and lets callers
     enable the feature explicitly.

  3. When we send children the cleanup signal, we send all
     the signals first, before waiting on any children. This
     is to avoid the case where one child might be waiting
     on another one to exit, causing a deadlock. We inform
     all of them that it's time to die before reaping any.

     In practice, there is only ever one dashed external run
     from a given process, so this doesn't matter much now.
     But it future-proofs us if other callers start using
     the wait_after_clean mechanism.

There's no automated test here, because it would end up racy
and unportable. But it's easy to reproduce the situation by
running the log command given above and hitting ^C.

Signed-off-by: Jeff King <peff@peff.net>
---
 git.c         |  1 +
 run-command.c | 19 +++++++++++++++++++
 run-command.h |  1 +
 3 files changed, 21 insertions(+)

diff --git a/git.c b/git.c
index bc2f2a7ec9..c8fe6637df 100644
--- a/git.c
+++ b/git.c
@@ -588,6 +588,7 @@ static void execv_dashed_external(const char **argv)
 	argv_array_pushf(&cmd.args, "git-%s", argv[0]);
 	argv_array_pushv(&cmd.args, argv + 1);
 	cmd.clean_on_exit = 1;
+	cmd.wait_after_clean = 1;
 	cmd.silent_exec_failure = 1;
 
 	trace_argv_printf(cmd.args.argv, "trace: exec:");
diff --git a/run-command.c b/run-command.c
index ca905a9e80..73bfba7ef9 100644
--- a/run-command.c
+++ b/run-command.c
@@ -29,6 +29,8 @@ static int installed_child_cleanup_handler;
 
 static void cleanup_children(int sig, int in_signal)
 {
+	struct child_to_clean *children_to_wait_for = NULL;
+
 	while (children_to_clean) {
 		struct child_to_clean *p = children_to_clean;
 		children_to_clean = p->next;
@@ -45,6 +47,23 @@ static void cleanup_children(int sig, int in_signal)
 		}
 
 		kill(p->pid, sig);
+
+		if (p->process->wait_after_clean) {
+			p->next = children_to_wait_for;
+			children_to_wait_for = p;
+		} else {
+			if (!in_signal)
+				free(p);
+		}
+	}
+
+	while (children_to_wait_for) {
+		struct child_to_clean *p = children_to_wait_for;
+		children_to_wait_for = p->next;
+
+		while (waitpid(p->pid, NULL, 0) < 0 && errno == EINTR)
+			; /* spin waiting for process exit or error */
+
 		if (!in_signal)
 			free(p);
 	}
diff --git a/run-command.h b/run-command.h
index dd1c78c28d..4fa8f65adb 100644
--- a/run-command.h
+++ b/run-command.h
@@ -43,6 +43,7 @@ struct child_process {
 	unsigned stdout_to_stderr:1;
 	unsigned use_shell:1;
 	unsigned clean_on_exit:1;
+	unsigned wait_after_clean:1;
 	void (*clean_on_exit_handler)(struct child_process *process);
 	void *clean_on_exit_handler_cbdata;
 };
-- 
2.11.0.527.gfef230ca76

^ permalink raw reply related

* Re: [PATCH] branch_get_push: do not segfault when HEAD is detached
From: Kyle Meyer @ 2017-01-07  1:19 UTC (permalink / raw)
  To: Jeff King; +Cc: Johannes Schindelin, git
In-Reply-To: <20170107011138.uuy6ob234kyy3y4e@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Fri, Jan 06, 2017 at 08:09:32PM -0500, Kyle Meyer wrote:
>
>> > $ git grep -c HEAD^{} junio/pu -- t/
>> > junio/pu:t/t3200-branch.sh:3
>> >
>> > Maybe use HEAD^0 just for consistency?
>>
>> Yes, thanks for pointing that out.
>
> The other option is just "git checkout --detach", which is also used in
> the test suite. I tend to prefer it because it's a little more obvious
> to a reader.

True, that does seem clearer.  Seems I should've waited a bit before
sending out v2.

--
Kyle

^ permalink raw reply

* [PATCH 2/3] execv_dashed_external: stop exiting with negative code
From: Jeff King @ 2017-01-07  1:17 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <20170107011445.3e4fv6vdtimrwhgv@sigill.intra.peff.net>

When we try to exec a git sub-command, we pass along the
status code from run_command(). But that may return -1 if we
ran into an error with pipe() or execve(). This tends to
work (and end up as 255 due to twos-complement wraparound
and truncation), but in general it's probably a good idea to
avoid negative exit codes for portability.

We can easily translate to the normal generic "128" code we
get when syscalls cause us to die.

Signed-off-by: Jeff King <peff@peff.net>
---
I know that negative exit codes were a problem once upon a time on
Windows, but I think that is fine since 47e3de0e79 (MinGW: truncate
exit()'s argument to lowest 8 bits, 2009-07-05). Still, I think it's a
weird portability thing that we are better off avoiding (and certainly I
wouldn't be surprised if some callers assume everything >128 is a
signal).

 git.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/git.c b/git.c
index d0e04d5c97..bc2f2a7ec9 100644
--- a/git.c
+++ b/git.c
@@ -593,12 +593,16 @@ static void execv_dashed_external(const char **argv)
 	trace_argv_printf(cmd.args.argv, "trace: exec:");
 
 	/*
-	 * if we fail because the command is not found, it is
-	 * OK to return. Otherwise, we just pass along the status code.
+	 * If we fail because the command is not found, it is
+	 * OK to return. Otherwise, we just pass along the status code,
+	 * or our usual generic code if we were not even able to exec
+	 * the program.
 	 */
 	status = run_command(&cmd);
-	if (status >= 0 || errno != ENOENT)
+	if (status >= 0)
 		exit(status);
+	else if (errno != ENOENT)
+		exit(128);
 }
 
 static int run_argv(int *argcp, const char ***argv)
-- 
2.11.0.527.gfef230ca76


^ permalink raw reply related

* [PATCH 1/3] execv_dashed_external: use child_process struct
From: Jeff King @ 2017-01-07  1:16 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <20170107011445.3e4fv6vdtimrwhgv@sigill.intra.peff.net>

When we run a dashed external, we use the one-liner
run_command_v_opt() to do so. Let's switch to using a
child_process struct, which has two advantages:

  1. We can drop all of the allocation and cleanup code for
     building our custom argv array, and just rely on the
     builtin argv_array (at the minor cost of doing a few
     extra mallocs).

  2. We have access to the complete range of child_process
     options, not just the ones that the "_opt()" form can
     forward.

Signed-off-by: Jeff King <peff@peff.net>
---

It's possible people may disagree with reason (2), and we should add a
new RUN_WAIT_AFTER_CLEAN flag in the final patch. IMHO the whole
run_command_v_opt() thing is a good sign that you should be switching to
a child_process struct. :)

 git.c | 25 +++++++------------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/git.c b/git.c
index dce529fcbf..d0e04d5c97 100644
--- a/git.c
+++ b/git.c
@@ -575,8 +575,7 @@ static void handle_builtin(int argc, const char **argv)
 
 static void execv_dashed_external(const char **argv)
 {
-	struct strbuf cmd = STRBUF_INIT;
-	const char *tmp;
+	struct child_process cmd = CHILD_PROCESS_INIT;
 	int status;
 
 	if (get_super_prefix())
@@ -586,30 +585,20 @@ static void execv_dashed_external(const char **argv)
 		use_pager = check_pager_config(argv[0]);
 	commit_pager_choice();
 
-	strbuf_addf(&cmd, "git-%s", argv[0]);
+	argv_array_pushf(&cmd.args, "git-%s", argv[0]);
+	argv_array_pushv(&cmd.args, argv + 1);
+	cmd.clean_on_exit = 1;
+	cmd.silent_exec_failure = 1;
 
-	/*
-	 * argv[0] must be the git command, but the argv array
-	 * belongs to the caller, and may be reused in
-	 * subsequent loop iterations. Save argv[0] and
-	 * restore it on error.
-	 */
-	tmp = argv[0];
-	argv[0] = cmd.buf;
-
-	trace_argv_printf(argv, "trace: exec:");
+	trace_argv_printf(cmd.args.argv, "trace: exec:");
 
 	/*
 	 * if we fail because the command is not found, it is
 	 * OK to return. Otherwise, we just pass along the status code.
 	 */
-	status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
+	status = run_command(&cmd);
 	if (status >= 0 || errno != ENOENT)
 		exit(status);
-
-	argv[0] = tmp;
-
-	strbuf_release(&cmd);
 }
 
 static int run_argv(int *argcp, const char ***argv)
-- 
2.11.0.527.gfef230ca76


^ permalink raw reply related

* [PATCH 0/3] fix ^C killing pager when running alias
From: Jeff King @ 2017-01-07  1:14 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <20170106232042.ptn6grtll5wpxhc4@sigill.intra.peff.net>

On Fri, Jan 06, 2017 at 06:20:42PM -0500, Jeff King wrote:

> > In general, I think it is wrong to wait for child processes when a signal
> > was received. After all, it is the purpose of a (deadly) signal to have the
> > process go away. There may be programs that know it better, like less, but
> > git should not attempt to know better in general.
> > 
> > We do apply some special behavior for certain cases like we do for the
> > pager. And now the case with aliases is another special situation. The
> > parent git process only delegates to the child, and as such it is reasonable
> > that it binds its life time to the first child, which executes the expanded
> > alias.
> 
> Yeah, I think I agree. That binding is something you want in many cases,
> but not necessarily all. The original purpose of clean_on_exit was to
> create a binding like that, but of course it can be (and with the
> smudge-filter stuff, arguably has been) used for other cases, too.
> 
> I'll work up a patch that makes it a separate option, which should be
> pretty easy.

Yeah, this did turn out to be really easy. I spent most of the time
trying to explain the issue in the commit message in a sane way.
Hopefully it didn't end up _too_ long. :)

The interesting bit is in the third one. The first is a necessary
preparatory step, and the second is a cleanup I noticed in the
neighborhood.

  [1/3]: execv_dashed_external: use child_process struct
  [2/3]: execv_dashed_external: stop exiting with negative code
  [3/3]: execv_dashed_external: wait for child on signal death

 git.c         | 36 +++++++++++++++---------------------
 run-command.c | 19 +++++++++++++++++++
 run-command.h |  1 +
 3 files changed, 35 insertions(+), 21 deletions(-)

-Peff

^ permalink raw reply

* [PATCH v2] branch_get_push: do not segfault when HEAD is detached
From: Kyle Meyer @ 2017-01-07  1:12 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Johannes Schindelin, Kyle Meyer
In-Reply-To: <20170106045623.21118-1-kyle@kyleam.com>

Move the detached HEAD check from branch_get_push_1() to
branch_get_push() to avoid setting branch->push_tracking_ref when
branch is NULL.

Signed-off-by: Kyle Meyer <kyle@kyleam.com>
---
 remote.c                  | 6 +++---
 t/t1514-rev-parse-push.sh | 6 ++++++
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/remote.c b/remote.c
index ad6c5424e..d5eaec737 100644
--- a/remote.c
+++ b/remote.c
@@ -1716,9 +1716,6 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
 {
 	struct remote *remote;
 
-	if (!branch)
-		return error_buf(err, _("HEAD does not point to a branch"));
-
 	remote = remote_get(pushremote_for_branch(branch, NULL));
 	if (!remote)
 		return error_buf(err,
@@ -1778,6 +1775,9 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
 
 const char *branch_get_push(struct branch *branch, struct strbuf *err)
 {
+	if (!branch)
+		return error_buf(err, _("HEAD does not point to a branch"));
+
 	if (!branch->push_tracking_ref)
 		branch->push_tracking_ref = branch_get_push_1(branch, err);
 	return branch->push_tracking_ref;
diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh
index 7214f5b33..623a32aa6 100755
--- a/t/t1514-rev-parse-push.sh
+++ b/t/t1514-rev-parse-push.sh
@@ -60,4 +60,10 @@ test_expect_success '@{push} with push refspecs' '
 	resolve topic@{push} refs/remotes/origin/magic/topic
 '
 
+test_expect_success 'resolving @{push} fails with a detached HEAD' '
+	git checkout HEAD^0 &&
+	test_when_finished "git checkout -" &&
+	test_must_fail git rev-parse @{push}
+'
+
 test_done
-- 
2.11.0


^ permalink raw reply related

* Re: [PATCH] branch_get_push: do not segfault when HEAD is detached
From: Jeff King @ 2017-01-07  1:11 UTC (permalink / raw)
  To: Kyle Meyer; +Cc: Johannes Schindelin, git
In-Reply-To: <87inprllpv.fsf@kyleam.com>

On Fri, Jan 06, 2017 at 08:09:32PM -0500, Kyle Meyer wrote:

> > $ git grep -c HEAD^{} junio/pu -- t/
> > junio/pu:t/t3200-branch.sh:3
> >
> > Maybe use HEAD^0 just for consistency?
> 
> Yes, thanks for pointing that out.

The other option is just "git checkout --detach", which is also used in
the test suite. I tend to prefer it because it's a little more obvious
to a reader.

-Peff

^ permalink raw reply

* Re: [PATCH] branch_get_push: do not segfault when HEAD is detached
From: Kyle Meyer @ 2017-01-07  1:09 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Jeff King
In-Reply-To: <alpine.DEB.2.20.1701061438300.3469@virtualbox>

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

[...]

>> +test_expect_success 'resolving @{push} fails with a detached HEAD' '
>> +	git checkout HEAD^{} &&
>
> I seem to recall that we prefer HEAD^0 over HEAD^{} and the existing
> scripts seem to agree with me:
>
> $ git grep -c HEAD^0 junio/pu -- t/
> junio/pu:t/t1450-fsck.sh:1
> junio/pu:t/t1507-rev-parse-upstream.sh:2
> junio/pu:t/t2020-checkout-detach.sh:5
> junio/pu:t/t3200-branch.sh:1
> junio/pu:t/t3203-branch-output.sh:3
> junio/pu:t/t3400-rebase.sh:1
> junio/pu:t/t3404-rebase-interactive.sh:1
> junio/pu:t/t5407-post-rewrite-hook.sh:2
> junio/pu:t/t5505-remote.sh:1
> junio/pu:t/t5510-fetch.sh:1
> junio/pu:t/t5533-push-cas.sh:3
> junio/pu:t/t6035-merge-dir-to-symlink.sh:3
> junio/pu:t/t7201-co.sh:2
> junio/pu:t/t7402-submodule-rebase.sh:1
> junio/pu:t/t9105-git-svn-commit-diff.sh:1
> junio/pu:t/t9107-git-svn-migrate.sh:1
>
> $ git grep -c HEAD^{} junio/pu -- t/
> junio/pu:t/t3200-branch.sh:3
>
> Maybe use HEAD^0 just for consistency?

Yes, thanks for pointing that out.

--
Kyle

^ permalink raw reply

* Re: [PATCH] submodule update --init: displays correct path from submodule
From: Stefan Beller @ 2017-01-07  1:01 UTC (permalink / raw)
  To: David Turner; +Cc: Junio C Hamano, git@vger.kernel.org
In-Reply-To: <1483750501.31165.9.camel@frank>

On Fri, Jan 6, 2017 at 4:55 PM, David Turner <novalis@novalis.org> wrote:
> (for the test)
> Signed-off-by: David Turner <dturner@twosigma.com>
>
> TIL: super-prefix!

yeah that was introduced recently for prefixes from "outside the repository"
e.g. a superproject, its first user is grep --recurse-submodules.

Also I realize I have set diff.context to 15 as I was experimenting
with it earlier,
and forgot to turn it off. So a lot of context in the diff for the patch.

Thanks for the bug report,
Stefan

^ permalink raw reply

* Re: [PATCH] submodule update --init: displays correct path from submodule
From: David Turner @ 2017-01-07  0:55 UTC (permalink / raw)
  To: Stefan Beller; +Cc: gitster, git
In-Reply-To: <20170107001953.3196-1-sbeller@google.com>

(for the test)
Signed-off-by: David Turner <dturner@twosigma.com>

TIL: super-prefix!


On Fri, 2017-01-06 at 16:19 -0800, Stefan Beller wrote:
> In the submodule helper we did not correctly handled the display path
> for initializing submodules when both the submodule is inside a
> subdirectory as well as the command being invoked from a subdirectory
> (as viewed from the superproject).
> 
> This was broken in 3604242f080, which was written at a time where
> there was no super-prefix available, so we abused the --prefix option
> for the same purpose and could get only one case right (the call from
> within a subdirectory, not the submodule being in a subdirectory).
> 
> Test-provided-by: David Turner <novalis@novalis.org>
> Signed-off-by: Stefan Beller <sbeller@google.com>
> ---
> 
>   applies on sb/submodule-embed-gitdir as that contains 89c862655
>   (submodule helper: support super prefix)
> 
>  builtin/submodule--helper.c | 13 +++++++------
>  git-submodule.sh            |  2 +-
>  t/t7406-submodule-update.sh | 17 +++++++++++++++++
>  3 files changed, 25 insertions(+), 7 deletions(-)
> 
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 242d9911a6..7b3f9fc293 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -305,32 +305,36 @@ static int module_list(int argc, const char **argv, const char *prefix)
>  
>  		utf8_fprintf(stdout, "%s\n", ce->name);
>  	}
>  	return 0;
>  }
>  
>  static void init_submodule(const char *path, const char *prefix, int quiet)
>  {
>  	const struct submodule *sub;
>  	struct strbuf sb = STRBUF_INIT;
>  	char *upd = NULL, *url = NULL, *displaypath;
>  
>  	/* Only loads from .gitmodules, no overlay with .git/config */
>  	gitmodules_config();
>  
> -	if (prefix) {
> -		strbuf_addf(&sb, "%s%s", prefix, path);
> +	if (prefix && get_super_prefix())
> +		die("BUG: cannot have prefix and superprefix");
> +	else if (prefix)
> +		displaypath = xstrdup(relative_path(path, prefix, &sb));
> +	else if (get_super_prefix()) {
> +		strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
>  		displaypath = strbuf_detach(&sb, NULL);
>  	} else
>  		displaypath = xstrdup(path);
>  
>  	sub = submodule_from_path(null_sha1, path);
>  
>  	if (!sub)
>  		die(_("No url found for submodule path '%s' in .gitmodules"),
>  			displaypath);
>  
>  	/*
>  	 * Copy url setting when it is not set yet.
>  	 * To look up the url in .git/config, we must not fall back to
>  	 * .gitmodules, so look it up directly.
>  	 */
> @@ -391,33 +395,30 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
>  	}
>  	strbuf_release(&sb);
>  	free(displaypath);
>  	free(url);
>  	free(upd);
>  }
>  
>  static int module_init(int argc, const char **argv, const char *prefix)
>  {
>  	struct pathspec pathspec;
>  	struct module_list list = MODULE_LIST_INIT;
>  	int quiet = 0;
>  	int i;
>  
>  	struct option module_init_options[] = {
> -		OPT_STRING(0, "prefix", &prefix,
> -			   N_("path"),
> -			   N_("alternative anchor for relative paths")),
>  		OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
>  		OPT_END()
>  	};
>  
>  	const char *const git_submodule_helper_usage[] = {
>  		N_("git submodule--helper init [<path>]"),
>  		NULL
>  	};
>  
>  	argc = parse_options(argc, argv, prefix, module_init_options,
>  			     git_submodule_helper_usage, 0);
>  
>  	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
>  		return 1;
>  
> @@ -1117,31 +1118,31 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
>  
>  struct cmd_struct {
>  	const char *cmd;
>  	int (*fn)(int, const char **, const char *);
>  	unsigned option;
>  };
>  
>  static struct cmd_struct commands[] = {
>  	{"list", module_list, 0},
>  	{"name", module_name, 0},
>  	{"clone", module_clone, 0},
>  	{"update-clone", update_clone, 0},
>  	{"relative-path", resolve_relative_path, 0},
>  	{"resolve-relative-url", resolve_relative_url, 0},
>  	{"resolve-relative-url-test", resolve_relative_url_test, 0},
> -	{"init", module_init, 0},
> +	{"init", module_init, SUPPORT_SUPER_PREFIX},
>  	{"remote-branch", resolve_remote_submodule_branch, 0},
>  	{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
>  };
>  
>  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
>  {
>  	int i;
>  	if (argc < 2)
>  		die(_("submodule--helper subcommand must be "
>  		      "called with a subcommand"));
>  
>  	for (i = 0; i < ARRAY_SIZE(commands); i++) {
>  		if (!strcmp(argv[1], commands[i].cmd)) {
>  			if (get_super_prefix() &&
>  			    !(commands[i].option & SUPPORT_SUPER_PREFIX))
> diff --git a/git-submodule.sh b/git-submodule.sh
> index 9285b5c43d..4e47ff8ad8 100755
> --- a/git-submodule.sh
> +++ b/git-submodule.sh
> @@ -362,31 +362,31 @@ cmd_init()
>  			;;
>  		--)
>  			shift
>  			break
>  			;;
>  		-*)
>  			usage
>  			;;
>  		*)
>  			break
>  			;;
>  		esac
>  		shift
>  	done
>  
> -	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} "$@"
> +	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet}  "$@"
>  }
>  
>  #
>  # Unregister submodules from .git/config and remove their work tree
>  #
>  cmd_deinit()
>  {
>  	# parse $args after "submodule ... deinit".
>  	deinit_all=
>  	while test $# -ne 0
>  	do
>  		case "$1" in
>  		-f|--force)
>  			force=$1
>  			;;
> diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
> index 64f322c4cc..725bbed1f8 100755
> --- a/t/t7406-submodule-update.sh
> +++ b/t/t7406-submodule-update.sh
> @@ -128,30 +128,47 @@ done.
>  Cloning into '$pwd/recursivesuper/super/submodule'...
>  done.
>  EOF
>  
>  test_expect_success 'submodule update --init --recursive from subdirectory' '
>  	git -C recursivesuper/super reset --hard HEAD^ &&
>  	(cd recursivesuper &&
>  	 mkdir tmp &&
>  	 cd tmp &&
>  	 git submodule update --init --recursive ../super >../../actual 2>../../actual2
>  	) &&
>  	test_i18ncmp expect actual &&
>  	test_i18ncmp expect2 actual2
>  '
>  
> +cat <<EOF >expect2
> +Submodule 'foo/sub' ($pwd/withsubs/../rebasing) registered for path 'sub'
> +EOF
> +
> +test_expect_success 'submodule update --init from and of subdirectory' '
> +	git init withsubs &&
> +	(cd withsubs &&
> +	 mkdir foo &&
> +	 git submodule add "$(pwd)/../rebasing" foo/sub &&
> +	 (cd foo &&
> +	  git submodule deinit -f sub &&
> +	  git submodule update --init sub 2>../../actual2
> +	 )
> +	) &&
> +	test_i18ncmp expect2 actual2
> +'
> +
>  apos="'";
>  test_expect_success 'submodule update does not fetch already present commits' '
>  	(cd submodule &&
>  	  echo line3 >> file &&
>  	  git add file &&
>  	  test_tick &&
>  	  git commit -m "upstream line3"
>  	) &&
>  	(cd super/submodule &&
>  	  head=$(git rev-parse --verify HEAD) &&
>  	  echo "Submodule path ${apos}submodule$apos: checked out $apos$head$apos" > ../../expected &&
>  	  git reset --hard HEAD~1
>  	) &&
>  	(cd super &&
>  	  git submodule update > ../actual 2> ../actual.err



^ permalink raw reply

* [PATCH] submodule update --init: displays correct path from submodule
From: Stefan Beller @ 2017-01-07  0:19 UTC (permalink / raw)
  To: gitster; +Cc: novalis, git, Stefan Beller

In the submodule helper we did not correctly handled the display path
for initializing submodules when both the submodule is inside a
subdirectory as well as the command being invoked from a subdirectory
(as viewed from the superproject).

This was broken in 3604242f080, which was written at a time where
there was no super-prefix available, so we abused the --prefix option
for the same purpose and could get only one case right (the call from
within a subdirectory, not the submodule being in a subdirectory).

Test-provided-by: David Turner <novalis@novalis.org>
Signed-off-by: Stefan Beller <sbeller@google.com>
---

  applies on sb/submodule-embed-gitdir as that contains 89c862655
  (submodule helper: support super prefix)

 builtin/submodule--helper.c | 13 +++++++------
 git-submodule.sh            |  2 +-
 t/t7406-submodule-update.sh | 17 +++++++++++++++++
 3 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 242d9911a6..7b3f9fc293 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -305,32 +305,36 @@ static int module_list(int argc, const char **argv, const char *prefix)
 
 		utf8_fprintf(stdout, "%s\n", ce->name);
 	}
 	return 0;
 }
 
 static void init_submodule(const char *path, const char *prefix, int quiet)
 {
 	const struct submodule *sub;
 	struct strbuf sb = STRBUF_INIT;
 	char *upd = NULL, *url = NULL, *displaypath;
 
 	/* Only loads from .gitmodules, no overlay with .git/config */
 	gitmodules_config();
 
-	if (prefix) {
-		strbuf_addf(&sb, "%s%s", prefix, path);
+	if (prefix && get_super_prefix())
+		die("BUG: cannot have prefix and superprefix");
+	else if (prefix)
+		displaypath = xstrdup(relative_path(path, prefix, &sb));
+	else if (get_super_prefix()) {
+		strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
 		displaypath = strbuf_detach(&sb, NULL);
 	} else
 		displaypath = xstrdup(path);
 
 	sub = submodule_from_path(null_sha1, path);
 
 	if (!sub)
 		die(_("No url found for submodule path '%s' in .gitmodules"),
 			displaypath);
 
 	/*
 	 * Copy url setting when it is not set yet.
 	 * To look up the url in .git/config, we must not fall back to
 	 * .gitmodules, so look it up directly.
 	 */
@@ -391,33 +395,30 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
 	}
 	strbuf_release(&sb);
 	free(displaypath);
 	free(url);
 	free(upd);
 }
 
 static int module_init(int argc, const char **argv, const char *prefix)
 {
 	struct pathspec pathspec;
 	struct module_list list = MODULE_LIST_INIT;
 	int quiet = 0;
 	int i;
 
 	struct option module_init_options[] = {
-		OPT_STRING(0, "prefix", &prefix,
-			   N_("path"),
-			   N_("alternative anchor for relative paths")),
 		OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
 		OPT_END()
 	};
 
 	const char *const git_submodule_helper_usage[] = {
 		N_("git submodule--helper init [<path>]"),
 		NULL
 	};
 
 	argc = parse_options(argc, argv, prefix, module_init_options,
 			     git_submodule_helper_usage, 0);
 
 	if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
 		return 1;
 
@@ -1117,31 +1118,31 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 
 struct cmd_struct {
 	const char *cmd;
 	int (*fn)(int, const char **, const char *);
 	unsigned option;
 };
 
 static struct cmd_struct commands[] = {
 	{"list", module_list, 0},
 	{"name", module_name, 0},
 	{"clone", module_clone, 0},
 	{"update-clone", update_clone, 0},
 	{"relative-path", resolve_relative_path, 0},
 	{"resolve-relative-url", resolve_relative_url, 0},
 	{"resolve-relative-url-test", resolve_relative_url_test, 0},
-	{"init", module_init, 0},
+	{"init", module_init, SUPPORT_SUPER_PREFIX},
 	{"remote-branch", resolve_remote_submodule_branch, 0},
 	{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	if (argc < 2)
 		die(_("submodule--helper subcommand must be "
 		      "called with a subcommand"));
 
 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
 		if (!strcmp(argv[1], commands[i].cmd)) {
 			if (get_super_prefix() &&
 			    !(commands[i].option & SUPPORT_SUPER_PREFIX))
diff --git a/git-submodule.sh b/git-submodule.sh
index 9285b5c43d..4e47ff8ad8 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -362,31 +362,31 @@ cmd_init()
 			;;
 		--)
 			shift
 			break
 			;;
 		-*)
 			usage
 			;;
 		*)
 			break
 			;;
 		esac
 		shift
 	done
 
-	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper init ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} "$@"
+	git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper init ${GIT_QUIET:+--quiet}  "$@"
 }
 
 #
 # Unregister submodules from .git/config and remove their work tree
 #
 cmd_deinit()
 {
 	# parse $args after "submodule ... deinit".
 	deinit_all=
 	while test $# -ne 0
 	do
 		case "$1" in
 		-f|--force)
 			force=$1
 			;;
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 64f322c4cc..725bbed1f8 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -128,30 +128,47 @@ done.
 Cloning into '$pwd/recursivesuper/super/submodule'...
 done.
 EOF
 
 test_expect_success 'submodule update --init --recursive from subdirectory' '
 	git -C recursivesuper/super reset --hard HEAD^ &&
 	(cd recursivesuper &&
 	 mkdir tmp &&
 	 cd tmp &&
 	 git submodule update --init --recursive ../super >../../actual 2>../../actual2
 	) &&
 	test_i18ncmp expect actual &&
 	test_i18ncmp expect2 actual2
 '
 
+cat <<EOF >expect2
+Submodule 'foo/sub' ($pwd/withsubs/../rebasing) registered for path 'sub'
+EOF
+
+test_expect_success 'submodule update --init from and of subdirectory' '
+	git init withsubs &&
+	(cd withsubs &&
+	 mkdir foo &&
+	 git submodule add "$(pwd)/../rebasing" foo/sub &&
+	 (cd foo &&
+	  git submodule deinit -f sub &&
+	  git submodule update --init sub 2>../../actual2
+	 )
+	) &&
+	test_i18ncmp expect2 actual2
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
 	(cd submodule &&
 	  echo line3 >> file &&
 	  git add file &&
 	  test_tick &&
 	  git commit -m "upstream line3"
 	) &&
 	(cd super/submodule &&
 	  head=$(git rev-parse --verify HEAD) &&
 	  echo "Submodule path ${apos}submodule$apos: checked out $apos$head$apos" > ../../expected &&
 	  git reset --hard HEAD~1
 	) &&
 	(cd super &&
 	  git submodule update > ../actual 2> ../actual.err
-- 
2.11.0.rc2.30.g7c4be45.dirty


^ permalink raw reply related

* Re: Regression: Ctrl-c from the pager in an alias exits it
From: Jeff King @ 2017-01-06 23:20 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <2ed6f78b-7704-c724-c99b-e310c383c4e8@kdbg.org>

On Fri, Jan 06, 2017 at 11:42:25PM +0100, Johannes Sixt wrote:

> > So I dunno. Maybe this waiting should be restricted only to certain
> > cases like executing git sub-commands.
> 
> If given it some thought.
> 
> In general, I think it is wrong to wait for child processes when a signal
> was received. After all, it is the purpose of a (deadly) signal to have the
> process go away. There may be programs that know it better, like less, but
> git should not attempt to know better in general.
> 
> We do apply some special behavior for certain cases like we do for the
> pager. And now the case with aliases is another special situation. The
> parent git process only delegates to the child, and as such it is reasonable
> that it binds its life time to the first child, which executes the expanded
> alias.

Yeah, I think I agree. That binding is something you want in many cases,
but not necessarily all. The original purpose of clean_on_exit was to
create a binding like that, but of course it can be (and with the
smudge-filter stuff, arguably has been) used for other cases, too.

I'll work up a patch that makes it a separate option, which should be
pretty easy.

-Peff

^ permalink raw reply

* Git filesystem case-insensitive to case-sensitive system broken
From: Steven Robertson @ 2017-01-06 21:56 UTC (permalink / raw)
  To: git

Hello,

I was doing development on a linux box on AWS, when we found a code
bug that had me switching to running the code on a Mac instead. We
discovered that we had accidentally named two files the same when
looked at case-insensitively, which made git commands afterwards
display the wrong thing. It looked like this (ignoring some things
that aren't relevant):

$ git status


   modified:   tests/test_system/show_19_L.txt


no changes added to commit (use "git add" and/or "git commit -a")

$ git checkout tests/test_system/show_19_L.txt

$ git status


   modified:   tests/test_system/show_19_l.txt


no changes added to commit (use "git add" and/or "git commit -a")

$ git checkout tests/test_system/show_19_l.txt

$ git status


   modified:   tests/test_system/show_19_L.txt


no changes added to commit (use "git add" and/or "git commit -a")

$ diff tests/test_system/show_19_L.txt tests/test_system/show_19_l.txt

$


Those two files are different in our repo, and as such git thinks that
we modified one of them when we try and pull it down from github
again.


Thanks for looking at this!
-- Steven

^ permalink raw reply

* Re: Regression: Ctrl-c from the pager in an alias exits it
From: Johannes Sixt @ 2017-01-06 22:42 UTC (permalink / raw)
  To: Jeff King; +Cc: Trygve Aaberge, Nguyễn Thái Ngọc Duy, git
In-Reply-To: <20170106194115.k5u5esv7t63mryvk@sigill.intra.peff.net>

Am 06.01.2017 um 20:41 schrieb Jeff King:
> On Fri, Jan 06, 2017 at 03:39:59PM +0100, Johannes Sixt wrote:
>
>>> diff --git a/run-command.c b/run-command.c
>>> index ca905a9e80..db47c429b7 100644
>>> --- a/run-command.c
>>> +++ b/run-command.c
>>> @@ -29,6 +29,8 @@ static int installed_child_cleanup_handler;
>>>
>>>  static void cleanup_children(int sig, int in_signal)
>>>  {
>>> +	struct child_to_clean *children_to_wait_for = NULL;
>>> +
>>>  	while (children_to_clean) {
>>>  		struct child_to_clean *p = children_to_clean;
>>>  		children_to_clean = p->next;
>>> @@ -45,6 +47,17 @@ static void cleanup_children(int sig, int in_signal)
>>>  		}
>>>
>>>  		kill(p->pid, sig);
>>> +		p->next = children_to_wait_for;
>>> +		children_to_wait_for = p;
>>> +	}
>>> +
>>> +	while (children_to_wait_for) {
>>> +		struct child_to_clean *p = children_to_wait_for;
>>> +		children_to_wait_for = p->next;
>>> +
>>> +		while (waitpid(p->pid, NULL, 0) < 0 && errno == EINTR)
>>> +			; /* spin waiting for process exit or error */
>>> +
>>>  		if (!in_signal)
>>>  			free(p);
>>>  	}
>>>
>>
>> This looks like the minimal change necessary. I wonder, though, whether the
>> new local variable is really required. Wouldn't it be sufficient to walk the
>> children_to_clean chain twice?
>
> Yeah, I considered that. The fact that we disassemble the list in the
> first loop has two side effects:
>
>   1. It lets us free the list as we go (for the !in_signal case).
>
>   2. If we were to get another signal, it makes us sort-of reentrant. We
>      will only kill and wait for each pid once.
>
> Obviously (1) moves down to the lower loop, but I was trying to preserve
> (2). I'm not sure if it is worth bothering, though.

Makes sense.

> The way we pull
> items off of the list is certainly not atomic (it does shorten the race
> to a few instructions, though, versus potentially waiting on waitpid()
> to return).
>
> My bigger concern with the whole thing is whether we could hit some sort
> of deadlock if the child doesn't die when we send it a signal. E.g.,
> imagine we have a pipe open to the child and somebody sends SIGTERM to
> us. We propagate SIGTERM to the child, and then waitpid() for it. The
> child decides to ignore our SIGTERM for some reason and keep reading
> until EOF on the pipe. It won't ever get it, and the two processes will
> hang forever.
>
> You can argue perhaps that the child is broken in that case. And I doubt
> this could trigger when running a git sub-command. But we may add more
> children in the future. Right now we use it for the new multi-file
> clean/smudge filters. They use the hook feature to close the
> descriptors, but note that that won't run in the in_signal case.
>
> So I dunno. Maybe this waiting should be restricted only to certain
> cases like executing git sub-commands.

If given it some thought.

In general, I think it is wrong to wait for child processes when a 
signal was received. After all, it is the purpose of a (deadly) signal 
to have the process go away. There may be programs that know it better, 
like less, but git should not attempt to know better in general.

We do apply some special behavior for certain cases like we do for the 
pager. And now the case with aliases is another special situation. The 
parent git process only delegates to the child, and as such it is 
reasonable that it binds its life time to the first child, which 
executes the expanded alias.

-- Hannes


^ permalink raw reply

* minor bug: git submodule update --init from a subdir shows wrong path
From: David Turner @ 2017-01-06 21:31 UTC (permalink / raw)
  To: git, sbeller

[-- Attachment #1: Type: text/plain, Size: 411 bytes --]

I believe (from bisection) that this was introduced in the transition to
C at 3604242f080.

I've attached a repro (against master).  At the time the bug was
introduced, the output in question went to stdout instead of stderr, so
the attached test case will not quite work on the older version.  But if
you run under -v, you'll be able to see the bad ("foo/foo/sub" instead
of "foo/sub" or just "sub") output.



[-- Attachment #2: wrong-submodule-path.patch --]
[-- Type: text/x-patch, Size: 917 bytes --]

diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 64f322c..e1deb17 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -140,6 +140,23 @@ test_expect_success 'submodule update --init --recursive from subdirectory' '
 	test_i18ncmp expect2 actual2
 '
 
+cat <<EOF >expect2
+Submodule 'foo/sub' (/home/novalis/twosigma/git/t/trash directory.t7406-submodule-update/withsubs/../rebasing) registered for path 'foo/sub'
+EOF
+
+test_expect_success 'submodule update --init from and of subdirectory' '
+	git init withsubs &&
+	(cd withsubs &&
+	 mkdir foo &&
+	 git submodule add "$(pwd)/../rebasing" foo/sub &&
+	 (cd foo &&
+	  git submodule deinit -f sub &&
+	  git submodule update --init sub 2>../../actual2
+	 )
+	) &&
+	test_i18ncmp expect2 actual2
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
 	(cd submodule &&

^ permalink raw reply related

* Re: Rebasing a branch with merges
From: Philip Oakley @ 2017-01-06 21:28 UTC (permalink / raw)
  To: Robert Dailey, Git; +Cc: Johannes Schindelin
In-Reply-To: <CAHd499BREpaHHyN89a1HchyJiQzPpdo3NSfoLLGVONEmX1m19g@mail.gmail.com>

From: "Robert Dailey" <rcdailey.lists@gmail.com>
> Here's the scenario:
>
> I create a topic branch so one other developer and myself can work on
> a feature that takes 2 weeks to complete. During that 2 week period,
> changes are occurring on master that I need in my topic branch. Since
> I have a collaborator on the branch, I opt for merges instead of
> rebase.
>
> Each day I merge from master to the topic branch, which changes code
> I'm actively working in and requires semantic changes (functions
> renamed, moved, etc).
>
> Once I'm ready to merge the topic branch back into master, I have two
> options (bearing in mind the goal is to keep history as clean as
> possible. Furthermore this implies that the constant merging into
> topic from master has made the topic branch look unwieldy and
> difficult to audit):

a broader question zero;
0. Is the merge always clean? Do you always do a preparatory fixup! to 
ensure that the merge will be clean?

Ensuring that the merge will be clean should greatly simplify your decision 
about process.

>
> 1. Do a squash merge, which keeps history clean but we lose context
> for the important bits (the commits representing units of work that
> contribute to the topic itself).
>
> 2. Do a final rebase prior to merging.
>
> #2 doesn't seem to be possible due to patch ordering. For example, if
> I have real commits after merge commits that depend on those changes
> from master being present as a base at that point in time, the rebase
> will cause the patch before it to no longer include those changes from
> master.

How much of the historic fixups to cover changes on master do you want to 
keep visible? i.e. how many fork-points are truly needed (a. by you, b. by 
the project - personal knowledge vs corporate knowledge).?

>
> Is there a mechanism to rebase in this situation to both achieve a
> clean, linear history for the topic branch and allow fast forward
> merging if desired, while still not causing superfluous conflicts due
> to the merges being omitted during the rebase?
>
> Thanks in advance for any advice in this scenario.
>

Have you looked at @dscho's garden-shears scripts that he uses on 
Git-for-Windows as he has to continuously rebase the Windows specific 
patches on top of the progressing Git master? Very similar issues ;-)

https://github.com/git-for-windows/build-extra/blob/master/shears.sh 
https://blogs.msdn.microsoft.com/visualstudioalm/2016/09/03/whats-new-in-git-for-windows-2-10/ 
(#Fun Facts)

--
Philip 


^ permalink raw reply

* [PATCH 2/5] unpack-trees: remove unneeded continue
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
  To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <20170106210330.31761-1-sbeller@google.com>

The continue is the last statement in the loop, so not needed.
This situation arose in 700e66d66 (2010-07-30, unpack-trees: let
read-tree -u remove index entries outside sparse area) when statements
after the continue were removed.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 unpack-trees.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index 55c75b4d6a..f16ef14294 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -272,31 +272,30 @@ static int check_updates(struct unpack_trees_options *o)
 
 		progress = start_progress_delay(_("Checking out files"),
 						total, 50, 1);
 		cnt = 0;
 	}
 
 	if (o->update)
 		git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
 	for (i = 0; i < index->cache_nr; i++) {
 		const struct cache_entry *ce = index->cache[i];
 
 		if (ce->ce_flags & CE_WT_REMOVE) {
 			display_progress(progress, ++cnt);
 			if (o->update && !o->dry_run)
 				unlink_entry(ce);
-			continue;
 		}
 	}
 	remove_marked_cache_entries(index);
 	remove_scheduled_dirs();
 
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
 		if (ce->ce_flags & CE_UPDATE) {
 			if (ce->ce_flags & CE_WT_REMOVE)
 				die("BUG: both update and delete flags are set on %s",
 				    ce->name);
 			display_progress(progress, ++cnt);
 			ce->ce_flags &= ~CE_UPDATE;
 			if (o->update && !o->dry_run) {
-- 
2.11.0.31.g919a8d0.dirty


^ permalink raw reply related

* [PATCH 3/5] unpack-trees: factor progress setup out of check_updates
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
  To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <20170106210330.31761-1-sbeller@google.com>

This makes check_updates shorter and easier to understand.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 unpack-trees.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index f16ef14294..971d091fd0 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -237,55 +237,63 @@ static void display_error_msgs(struct unpack_trees_options *o)
 }
 
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
 static void unlink_entry(const struct cache_entry *ce)
 {
 	if (!check_leading_path(ce->name, ce_namelen(ce)))
 		return;
 	if (remove_or_warn(ce->ce_mode, ce->name))
 		return;
 	schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
 
-static int check_updates(struct unpack_trees_options *o)
+static struct progress *get_progress(struct unpack_trees_options *o)
 {
 	unsigned cnt = 0, total = 0;
+	struct index_state *index = &o->result;
+
+	if (!o->update || !o->verbose_update)
+		return NULL;
+
+	for (; cnt < index->cache_nr; cnt++) {
+		const struct cache_entry *ce = index->cache[cnt];
+		if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
+			total++;
+	}
+
+	return start_progress_delay(_("Checking out files"),
+				    total, 50, 1);
+}
+
+static int check_updates(struct unpack_trees_options *o)
+{
+	unsigned cnt = 0;
 	int i, errs = 0;
 
 	struct progress *progress = NULL;
 	struct index_state *index = &o->result;
 	struct checkout state = CHECKOUT_INIT;
 
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
 	state.istate = index;
 
-	if (o->update && o->verbose_update) {
-		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
-			const struct cache_entry *ce = index->cache[cnt];
-			if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
-				total++;
-		}
-
-		progress = start_progress_delay(_("Checking out files"),
-						total, 50, 1);
-		cnt = 0;
-	}
+	progress = get_progress(o);
 
 	if (o->update)
 		git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
 	for (i = 0; i < index->cache_nr; i++) {
 		const struct cache_entry *ce = index->cache[i];
 
 		if (ce->ce_flags & CE_WT_REMOVE) {
 			display_progress(progress, ++cnt);
 			if (o->update && !o->dry_run)
 				unlink_entry(ce);
 		}
 	}
 	remove_marked_cache_entries(index);
 	remove_scheduled_dirs();
 
-- 
2.11.0.31.g919a8d0.dirty


^ permalink raw reply related

* [PATCH 4/5] unpack-trees: factor file removal out of check_updates
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
  To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <20170106210330.31761-1-sbeller@google.com>

This makes check_updates shorter and easier to understand.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 unpack-trees.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index 971d091fd0..b954ec1233 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -237,30 +237,50 @@ static void display_error_msgs(struct unpack_trees_options *o)
 }
 
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
 static void unlink_entry(const struct cache_entry *ce)
 {
 	if (!check_leading_path(ce->name, ce_namelen(ce)))
 		return;
 	if (remove_or_warn(ce->ce_mode, ce->name))
 		return;
 	schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
 
+static int remove_workingtree_files(struct unpack_trees_options *o,
+				    struct progress *progress)
+{
+	int i;
+	unsigned cnt = 0;
+	struct index_state *index = &o->result;
+
+	for (i = 0; i < index->cache_nr; i++) {
+		const struct cache_entry *ce = index->cache[i];
+
+		if (ce->ce_flags & CE_WT_REMOVE) {
+			display_progress(progress, ++cnt);
+			if (o->update && !o->dry_run)
+				unlink_entry(ce);
+		}
+	}
+
+	return cnt;
+}
+
 static struct progress *get_progress(struct unpack_trees_options *o)
 {
 	unsigned cnt = 0, total = 0;
 	struct index_state *index = &o->result;
 
 	if (!o->update || !o->verbose_update)
 		return NULL;
 
 	for (; cnt < index->cache_nr; cnt++) {
 		const struct cache_entry *ce = index->cache[cnt];
 		if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
 			total++;
 	}
 
 	return start_progress_delay(_("Checking out files"),
@@ -273,39 +293,32 @@ static int check_updates(struct unpack_trees_options *o)
 	int i, errs = 0;
 
 	struct progress *progress = NULL;
 	struct index_state *index = &o->result;
 	struct checkout state = CHECKOUT_INIT;
 
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
 	state.istate = index;
 
 	progress = get_progress(o);
 
 	if (o->update)
 		git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
-	for (i = 0; i < index->cache_nr; i++) {
-		const struct cache_entry *ce = index->cache[i];
 
-		if (ce->ce_flags & CE_WT_REMOVE) {
-			display_progress(progress, ++cnt);
-			if (o->update && !o->dry_run)
-				unlink_entry(ce);
-		}
-	}
+	cnt = remove_workingtree_files(o, progress);
 	remove_marked_cache_entries(index);
 	remove_scheduled_dirs();
 
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
 		if (ce->ce_flags & CE_UPDATE) {
 			if (ce->ce_flags & CE_WT_REMOVE)
 				die("BUG: both update and delete flags are set on %s",
 				    ce->name);
 			display_progress(progress, ++cnt);
 			ce->ce_flags &= ~CE_UPDATE;
 			if (o->update && !o->dry_run) {
 				errs |= checkout_entry(ce, &state, NULL);
 			}
-- 
2.11.0.31.g919a8d0.dirty


^ permalink raw reply related

* [PATCH 5/5] unpack-trees: factor working tree update out of check_updates
From: Stefan Beller @ 2017-01-06 21:03 UTC (permalink / raw)
  To: gitster, l.s.r; +Cc: git, Stefan Beller
In-Reply-To: <20170106210330.31761-1-sbeller@google.com>

This makes check_updates shorter and easier to understand.

Signed-off-by: Stefan Beller <sbeller@google.com>
---
 unpack-trees.c | 40 ++++++++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 14 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index b954ec1233..b40c069b1b 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -275,67 +275,79 @@ static struct progress *get_progress(struct unpack_trees_options *o)
 	struct index_state *index = &o->result;
 
 	if (!o->update || !o->verbose_update)
 		return NULL;
 
 	for (; cnt < index->cache_nr; cnt++) {
 		const struct cache_entry *ce = index->cache[cnt];
 		if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
 			total++;
 	}
 
 	return start_progress_delay(_("Checking out files"),
 				    total, 50, 1);
 }
 
-static int check_updates(struct unpack_trees_options *o)
+static int update_working_tree_files(struct unpack_trees_options *o,
+				     struct progress *progress,
+				     unsigned start_cnt)
 {
-	unsigned cnt = 0;
+	unsigned cnt = start_cnt;
 	int i, errs = 0;
 
-	struct progress *progress = NULL;
 	struct index_state *index = &o->result;
 	struct checkout state = CHECKOUT_INIT;
 
 	state.force = 1;
 	state.quiet = 1;
 	state.refresh_cache = 1;
 	state.istate = index;
 
-	progress = get_progress(o);
-
-	if (o->update)
-		git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
-
-	cnt = remove_workingtree_files(o, progress);
-	remove_marked_cache_entries(index);
-	remove_scheduled_dirs();
-
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
 		if (ce->ce_flags & CE_UPDATE) {
 			if (ce->ce_flags & CE_WT_REMOVE)
 				die("BUG: both update and delete flags are set on %s",
 				    ce->name);
 			display_progress(progress, ++cnt);
 			ce->ce_flags &= ~CE_UPDATE;
-			if (o->update && !o->dry_run) {
+			if (o->update && !o->dry_run)
 				errs |= checkout_entry(ce, &state, NULL);
-			}
 		}
 	}
+
+	return errs;
+}
+
+static int check_updates(struct unpack_trees_options *o)
+{
+	struct progress *progress = NULL;
+	struct index_state *index = &o->result;
+	int errs;
+	unsigned total_removed;
+
+	progress = get_progress(o);
+
+	if (o->update)
+		git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
+
+	total_removed = remove_workingtree_files(o, progress);
+	remove_marked_cache_entries(index);
+	remove_scheduled_dirs();
+	errs = update_working_tree_files(o, progress, total_removed);
+
 	stop_progress(&progress);
 	if (o->update)
 		git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
 	return errs != 0;
 }
 
 static int verify_uptodate_sparse(const struct cache_entry *ce,
 				  struct unpack_trees_options *o);
 static int verify_absent_sparse(const struct cache_entry *ce,
 				enum unpack_trees_error_types,
 				struct unpack_trees_options *o);
 
 static int apply_sparse_checkout(struct index_state *istate,
 				 struct cache_entry *ce,
 				 struct unpack_trees_options *o)
-- 
2.11.0.31.g919a8d0.dirty


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox