Re: [PATCH v2] t4150: fix broken test for am --scissors

2018-08-07 Thread Paul Tan
On Tue, Aug 7, 2018 at 1:42 AM, Andrei Rybak  wrote:
> On 2018-08-06 10:58, Paul Tan wrote:
>>> +   git commit -F msg-without-scissors-line &&
>>> +   git tag scissors-used &&
>>
>> Nit: I'm not quite sure about naming the tag "scissors-used" though,
>> since this commit was not created from the output of "git am
>> --scissors". Maybe it should be named `commit-without-scissors-line`
>> or something?
>>
>>> +   git commit -F msg-with-scissors-line &&
>>> +   git tag scissors-not-used &&
>>
>> Nit: Likewise, perhaps this tag could be named `commit-with-scissors-line`?
>
> How about "expected-for-scissors" and "expected-for-no-scissors"?

Yep that's fine.

Thanks,
Paul


Re: [PATCH v2] t4150: fix broken test for am --scissors

2018-08-06 Thread Paul Tan
Hi,

I've taken a look at the original test, and it is pretty broken. My
deepest apologies for this mess.

On Sun, Aug 5, 2018 at 2:10 AM, Andrei Rybak  wrote:
> Tests for "git am --[no-]scissors" [1] work in the following way:
>
>  1. Create files with commit messages
>  2. Use these files to create expected commits
>  3. Generate eml file with patch from expected commits
>  4. Create commits using git am with these eml files
>  5. Compare these commits with expected
>
> The test for "git am --scissors" is supposed to take a message with a
> scissors line and demonstrate that the subject line from the e-mail
> itself is overridden by the in-body "Subject:" header and that only text
> below the scissors line is included in the commit message of the commit
> created by the invocation of "git am --scissors". However, the setup of
> the test incorrectly uses a commit without the scissors line and in-body
> "Subject:" header in the commit message, and thus, creates eml file not
> suitable for testing of "git am --scissors".

I think what really happened was that I simply forgot that the first
line of the commit message would be pulled out into the formatted
patch's "Subject" header, and would thus not be affected by the
scissors line :-S.

> This can be checked by intentionally breaking is_scissors_line function
> in mailinfo.c, for example, by changing string ">8", which is used by
> the test. With such change the test should fail, but does not.

The main reason why the test still passes even with a broken
is_scissors_line() would be because it uses the wrong patch to pass to
"git am --scissors" -- it uses the patch _without_ a scissors line
rather than the patch _with_ the scissors line.

However, after fixing this problem, which I'll call problem (1), the
test will actually fail, due to:

(2) The trees of the commits `scissors` and `no-scissors` not being
identical, thus making test_cmp_rev fail even though the commit
messages of the commits are identical.

(3) As mentioned above, the test not accounting for the first line of
the commit message being used as the "Subject" header and thus not
affected by the scissors line.

So, there are 3 problems that will need to be fixed.

>
> Fix broken test by generating eml file with scissors line and in-body
> header "Subject:". Since the two tests for --scissors and --no-scissors
> options are there to test cutting or keeping the commit message, update
> both tests to change the test file in the same way, which allows us to
> generate only one eml file to be passed to git am. To clarify the
> intention of the test, give files and tags more explicit names.
>
> [1]: introduced in bf72ac17d (t4150: tests for am --[no-]scissors,
>  2015-07-19)
>
> Signed-off-by: Andrei Rybak 
> ---
>  t/t4150-am.sh | 39 ---
>  1 file changed, 20 insertions(+), 19 deletions(-)
>
> diff --git a/t/t4150-am.sh b/t/t4150-am.sh
> index e9b6f8158..bb2d951a7 100755
> --- a/t/t4150-am.sh
> +++ b/t/t4150-am.sh
> @@ -67,13 +67,15 @@ test_expect_success 'setup: messages' '
>
> EOF
>
> -   cat >scissors-msg <<-\EOF &&
> -   Test git-am with scissors line
> +   cat >msg-without-scissors-line <<-\EOF &&
> +   Test that git-am --scissors cuts at the scissors line
>
> This line should be included in the commit message.
> EOF
>
> -   cat - scissors-msg >no-scissors-msg <<-\EOF &&
> +   printf "Subject: " >subject-prefix &&
> +
> +   cat - subject-prefix msg-without-scissors-line 
> >msg-with-scissors-line <<-\EOF &&
> This line should not be included in the commit message with 
> --scissors enabled.
>
>  - - >8 - - remove everything above this line - - >8 - -

This fixes problem (3) by using an in-body header.

> @@ -150,18 +152,17 @@ test_expect_success setup '
> } >patch1-hg.eml &&
>
>
> -   echo scissors-file >scissors-file &&
> -   git add scissors-file &&
> -   git commit -F scissors-msg &&
> -   git tag scissors &&
> -   git format-patch --stdout scissors^ >scissors-patch.eml &&
> +   echo file >file &&
> +   git add file &&

This fixes the first half of problem (2) by making the naming of the
files the same.

> +   git commit -F msg-without-scissors-line &&
> +   git tag scissors-used &&

Nit: I'm not quite sure about naming the tag "scissors-used" though,
since this commit was not created from the output of "git am
--scissors". Maybe it should be named `commit-without-scissors-line`
or something?

This hunk removes the line:

git format-patch --stdout scissors^ >scissors-patch.eml &&

without a corresponding replacement, but that is fine because the test
should not be using a patch without a scissors line.

> git reset --hard HEAD^ &&
>
> -   echo no-scissors-file >no-scissors-file &&
> -   git add no-scissors-file &&
> -   git commit -F no-scissors-msg &&
> -   git tag no-scissors &&
> -   git format-patch 

Re: git-rebase --undo-skip proposal

2018-02-15 Thread Paul Tan
Hi Gabriel,

On Wed, Feb 14, 2018 at 4:42 AM, Stefan Beller <sbel...@google.com> wrote:
> On Tue, Feb 13, 2018 at 12:22 PM, Psidium Guajava <psiid...@gmail.com> wrote:
>>
>> Also, a little unrelated with this issue:
>> 5. What happened to the rewrite of rebase in C [2]? I couldn't find
>> any information after 2016.
>>
>> [1] https://public-inbox.org/git/201311011522.44631.tho...@koch.ro/
>> [2] 
>> https://public-inbox.org/git/1457779597-6918-1-git-send-email-pyoka...@gmail.com/
>
> cc'd Paul Tan, maybe he recalls the situation.

It was discarded in favor of Johannes' rebase-helper approach, and I
think parts of it are already in master. There's probably room for
help there.

I haven't had time to keep track of Git development, hence my
inactivity. Sorry about that.

Regards,
Paul


Re: [PATCH 1/5] am: Fix filename in safe_to_abort() error message

2016-12-08 Thread Paul Tan
Hi Stephan,

On Thu, Dec 8, 2016 at 5:51 AM, Stephan Beyer  wrote:
> diff --git a/builtin/am.c b/builtin/am.c
> index 6981f42ce..7cf40e6f2 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -2124,7 +2124,7 @@ static int safe_to_abort(const struct am_state *state)
>
> if (read_state_file(, state, "abort-safety", 1) > 0) {
> if (get_oid_hex(sb.buf, _safety))
> -   die(_("could not parse %s"), am_path(state, 
> "abort_safety"));
> +   die(_("could not parse %s"), am_path(state, 
> "abort-safety"));

Ah, this is obviously correct. Sorry for the oversight.

> } else
> oidclr(_safety);
>
> --
> 2.11.0.27.g4eed97c

Thanks,
Paul


Re: [PATCH 1/3] wt-status: implement opportunisitc index update correctly

2016-12-08 Thread Paul Tan
Hi Junio,

On Thu, Dec 8, 2016 at 4:48 AM, Stefan Beller  wrote:
> On Wed, Dec 7, 2016 at 11:41 AM, Junio C Hamano  wrote:
>> The require_clean_work_tree() function calls hold_locked_index()
>> with die_on_error=0 to signal that it is OK if it fails to obtain
>> the lock, but unconditionally calls update_index_if_able(), which
>> will try to write into fd=-1.
>>
>> Signed-off-by: Junio C Hamano 
>> ---

Ah, sorry about this. I was indeed misled by the function naming and
its comment ("do not complain if we can't"). Should have looked more
closely at the other call sites.

> However I think the promise of that function is
> to take care of the fd == -1?

Hmm, to add on, looking at the three other call sites of this
function, two of them (builtin/commit.c and builtin/describe.c)
basically do:

if (0 <= fd)
update_index_if_able(...)

with that 0 <= fd conditional. With this patch it becomes three out of
four. Perhaps the repeated use of this conditional is a sign that the
0 <= fd check could be built into update_index_if_able()? I think
there is precedent for building in these kind of checks --
rollback_lock_file() also does not fail if the lock file was not
successfully opened.

That said, the number of call sites is quite low so it's probably not
worth doing this.

Thanks,
Paul


Re: [PATCH 18/20] builtin/am: convert to struct object_id

2016-08-29 Thread Paul Tan
Hi Brian,

On Mon, Aug 29, 2016 at 7:27 AM, brian m. carlson
 wrote:
> Signed-off-by: brian m. carlson 
> ---
>  builtin/am.c | 138 
> +--
>  1 file changed, 69 insertions(+), 69 deletions(-)

I looked through this patch, and the conversion looks faithful and
straightforward to me. Just two minor comments:

> diff --git a/builtin/am.c b/builtin/am.c
> index 739b34dc..632d4288 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1053,10 +1053,10 @@ static void am_setup(struct am_state *state, enum 
> patch_format patch_format,
> else
> write_state_text(state, "applying", "");
>
> -   if (!get_sha1("HEAD", curr_head)) {
> -   write_state_text(state, "abort-safety", 
> sha1_to_hex(curr_head));
> +   if (!get_oid("HEAD", _head)) {
> +   write_state_text(state, "abort-safety", 
> oid_to_hex(_head));
> if (!state->rebasing)
> -   update_ref("am", "ORIG_HEAD", curr_head, NULL, 0,
> +   update_ref("am", "ORIG_HEAD", curr_head.hash, NULL, 0,
> UPDATE_REFS_DIE_ON_ERR);

I noticed that you used update_ref_oid() in other places of this
patch. Perhaps this should use update_ref_oid() as well for
consistency?

> @@ -1665,9 +1665,8 @@ static int fall_back_threeway(const struct am_state 
> *state, const char *index_pa
>   */
>  static void do_commit(const struct am_state *state)
>  {
> -   unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],
> - commit[GIT_SHA1_RAWSZ];
> -   unsigned char *ptr;
> +   struct object_id tree, parent, commit;
> +   struct object_id *ptr;

Ah, I just noticed that this is a very poorly named variable. Whoops.
Since we are here, should we rename this to something like "old_oid"?
Also, this should probably be a "const struct object_id *" as well, I
think.

Thanks,
Paul


Re: [PATCH] reset cached ident date before creating objects

2016-07-29 Thread Paul Tan
Hi Jeff,

On Sat, Jul 30, 2016 at 2:05 AM, Jeff King  wrote:
> When we compute the date to put in author/committer lines of
> commits, or tagger lines of tags, we get the current date
> once and then cache it for the rest of the program.  This is
> a good thing in some cases, like "git commit", because it
> means we do not racily assign different times to the
> author/committer fields of a single commit object.

So commits created with "git commit" should have the same author and
committer timestamps...

> diff --git a/commit.c b/commit.c
> index 71a360d..7ddbffe 100644
> --- a/commit.c
> +++ b/commit.c
> @@ -1548,6 +1548,7 @@ int commit_tree_extended(const char *msg, size_t 
> msg_len,
> }
>
> /* Person/date information */
> +   reset_ident_date();
> if (!author)
> author = git_author_info(IDENT_STRICT);
> strbuf_addf(, "author %s\n", author);

But since builtin/commit.c constructs its author ident string before
calling the editor and then commit_tree_extended(), this would cause
the resulting commits to have committer timestamps which differ from
their author timestamps.

So maybe we would have to put reset_ident_date() at the end of the
function instead, at least after git_committer_info() is called.

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 0/9] Let log-tree and friends respect diffopt's `file` field

2016-06-22 Thread Paul Tan
Hi Johannes,

On Tue, Jun 21, 2016 at 10:12 PM, Johannes Schindelin
<johannes.schinde...@gmx.de> wrote:
> Hi Paul,
>
> On Tue, 21 Jun 2016, Paul Tan wrote:
>
>> On Tue, Jun 21, 2016 at 6:34 PM, Johannes Schindelin
>> <johannes.schinde...@gmx.de> wrote:
>> > - this uncovered a problem with builtin am, where it asked the diff
>> >   machinery to close the file stream, but actually called the log_tree
>> >   machinery (which might mean that this patch series inadvertently fixes
>> >   a bug where `git am --rebasing` would write the commit message to
>> >   stdout instead of the `patch` file when erroring out)
>>
>> Please correct me if I'm wrong: looking at log-tree.c, the commit
>> message will not be printed when no_commit_id = 1, isn't it?
>> This is because we do not hit the code paths that write to stdout since
>> show_log() is not called.
>
> Why does builtin/am.c use log_tree_commit(), then? Why not simply run
> things through the diff machinery?

It's because git-am.sh called "git diff-tree", which in turn calls
log_tree_commit(), so to be safe I used the same function to ensure
that there were no unintended behavioral changes. e.g. what happened
with 3ecc704 (am --skip/--abort: merge HEAD/ORIG_HEAD tree into index,
2015-08-19)

Of course, it may be true that the diff machinery should be called
directly, although the code is quite involved so I can't really tell
the impact the change will have.

Thanks,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 0/9] Let log-tree and friends respect diffopt's `file` field

2016-06-21 Thread Paul Tan
Hi Johannes,

On Tue, Jun 21, 2016 at 6:34 PM, Johannes Schindelin
 wrote:
> - this uncovered a problem with builtin am, where it asked the diff
>   machinery to close the file stream, but actually called the log_tree
>   machinery (which might mean that this patch series inadvertently fixes
>   a bug where `git am --rebasing` would write the commit message to
>   stdout instead of the `patch` file when erroring out)

Please correct me if I'm wrong: looking at log-tree.c, the commit
message will not be printed when no_commit_id = 1, isn't it? This is
because we do not hit the code paths that write to stdout since
show_log() is not called.

Also, the return value of log_tree_commit() is actually a boolean
value, not an error status value, isn't it?

> This last point is a bigger issue, actually. There seem to be quite a
> few function calls in builtin/am.c whose return values that might
> indicate errors are flatly ignored. I see two calls to run_diff_index()
> whose return value then goes poof unchecked,

Thanks, future-proofing the builtin/am.c code is good, in case
run_diff_index() is updated to not call exit(128) on error in the
future.

> and several calls to
> write_state_text() and write_state_bool() with the same issue.

These functions will die() on error, so checking their error code is
not really useful. I'm not opposed to changing them to use the
write_file_gently() version though, although I don't see a need to
unless builtin/am.c is being libified.

> And I did
> not even try to review the code to that end, all I wanted was to verify
> that builtin am only has the close_file issue once (it does use it a
> second time, but that one is okay because it then calls
> run_diff_index(), i.e. the diff machinery).
>
> I am embarrassed to admit that these builtin am problems demonstrate
> that I, as a mentor of the builtin am project, failed to help make the
> patches as good as I expected myself to do.

Sorry to disappoint you :-(

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [BUG?] retrying with "am -3" doesn't work anymore

2016-03-29 Thread Paul Tan
Hi Jeff,

On Wed, Mar 30, 2016 at 10:15 AM, Jeff King  wrote:
> I noticed that I could not get a patch from Junio to apply earlier
> today, and I think it is a regression in the builtin git-am
> implementation.  I had trouble reproducing with a basic test case,
> though.
>
> Basically, I picked up the three patches from this sub-thread:
>
>   http://thread.gmane.org/gmane.comp.version-control.git/288987/focus=290222
>
> and tried to apply them on top of v2.8.0.
>
> Doing it with "git am -3 patches" works. But doing it with:
>
>   git am patches
>   git am -3
>
> doesn't. Bisecting shows that it did work before 783d7e8 (builtin-am:
> remove redirection to git-am.sh, 2015-08-04).

Yeah, with "git am -3" when resuming, the -3 will only affect the
current conflicting patch since 852a171 (am: let command-line options
override saved options, 2015-08-04).

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 05/17] rebase-options: implement rebase_options_load() and rebase_options_save()

2016-03-21 Thread Paul Tan
Hi Dscho,

(Sorry for the very late reply, I got caught up with some unexpected
work and am still clearing my inbox ><)

On Thu, Mar 17, 2016 at 1:11 AM, Johannes Schindelin
<johannes.schinde...@gmx.de> wrote:
> On Wed, 16 Mar 2016, Paul Tan wrote:
>> On Wed, Mar 16, 2016 at 4:04 PM, Johannes Schindelin
>> <johannes.schinde...@gmx.de> wrote:
>> > In addition I want to point out that sequencer's replay_opts seem to be at
>> > least related, but the patch shares none of its code with the sequencer.
>> > Let's avoid that.
>> >
>> > In other words, let's try to add as little code as possible when we can
>> > enhance existing code.
>>
>> Well, both git-rebase--am.sh and git-rebase--merge.sh do not use the
>> sequencer functionality at all, and we don't see git-am for example
>> needing to be aware of onto, orig-head, head-name etc.
>
> That is arguing that the implementation of --am and --merge is too far
> away from the sequencer and therefore should not be made closer.
>
> By that token, has_unstaged_changes() should never be allowed to call
> init_revisions(): it *never* looks at any revisions at all!
>
> And the idea of the sequencer is so much more related to --am and --merge
> than unstaged changes are to revisions: the entire purpose of the
> sequencer (no matter the *current* implementation with all its
> limitations) is to apply a bunch of patches, in sequence. That is pretty
> much precisely what *all* members of the rebase family are about.
>
> In other words: please do not let current limitations dictate that we
> should introduce diverging code for essentially the same workflow.

Ah, so you are thinking of replacing the --am and --merge scripts with
sequencer? That sounds great :-)

I'll wait for your sequencer patch series then.

Thanks!
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 05/17] rebase-options: implement rebase_options_load() and rebase_options_save()

2016-03-19 Thread Paul Tan
Hi Stefan,

On Tue, Mar 15, 2016 at 4:30 AM, Stefan Beller <sbel...@google.com> wrote:
> On Sat, Mar 12, 2016 at 2:46 AM, Paul Tan <pyoka...@gmail.com> wrote:
>> These functions can be used for loading and saving common rebase options
>> into a state directory.
>>
>> Signed-off-by: Paul Tan <pyoka...@gmail.com>
>> ---
>>  rebase-common.c | 69 
>> +
>>  rebase-common.h |  4 
>>  2 files changed, 73 insertions(+)
>>
>> diff --git a/rebase-common.c b/rebase-common.c
>> index 5a49ac4..1835f08 100644
>> --- a/rebase-common.c
>> +++ b/rebase-common.c
>> @@ -26,3 +26,72 @@ void rebase_options_swap(struct rebase_options *dst, 
>> struct rebase_options *src)
>> *dst = *src;
>> *src = tmp;
>>  }
>> +
>> +static int state_file_exists(const char *dir, const char *file)
>> +{
>> +   return file_exists(mkpath("%s/%s", dir, file));
>> +}
>
> How is this specific to the state file? All it does is create the
> leading directory
> if it doesn't exist? (So I'd expect file_exists(concat(dir, file)) to
> have the same
> result without actually creating the directory if it doesn't exist as
> a side effect?

I don't quite understand, AFAIK mkpath() does not create any
directories as a side-effect. And yes, I just wanted a short way to
say file_exists(concat(dir, file)) or file_exists(mkpath("%s/%s", dir,
file)) without cluttering up the code.

> If the dir doesn't exist it can be created in rebase_options_load explicitly?

I don't intend to create any directories if they do not exist.

>> +
>> +static int read_state_file(struct strbuf *sb, const char *dir, const char 
>> *file)
>> +{
>> +   const char *path = mkpath("%s/%s", dir, file);
>> +   strbuf_reset(sb);
>> +   if (strbuf_read_file(sb, path, 0) >= 0)
>> +   return sb->len;
>> +   else
>> +   return error(_("could not read '%s'"), path);
>> +}
>> +
>> +int rebase_options_load(struct rebase_options *opts, const char *dir)
>> +{
>> +   struct strbuf sb = STRBUF_INIT;
>> +   const char *filename;
>> +
>> +   /* opts->orig_refname */
>> +   if (read_state_file(, dir, "head-name") < 0)
>> +   return -1;
>> +   strbuf_trim();
>> +   if (starts_with(sb.buf, "refs/heads/"))
>> +   opts->orig_refname = strbuf_detach(, NULL);
>> +   else if (!strcmp(sb.buf, "detached HEAD"))
>> +   opts->orig_refname = NULL;
>> +   else
>> +   return error(_("could not parse %s"), mkpath("%s/%s", dir, 
>> "head-name"));
>> +
>> +   /* opts->onto */
>> +   if (read_state_file(, dir, "onto") < 0)
>> +   return -1;
>> +   strbuf_trim();
>> +   if (get_oid_hex(sb.buf, >onto) < 0)
>> +   return error(_("could not parse %s"), mkpath("%s/%s", dir, 
>> "onto"));
>> +
>> +   /*
>> +* We always write to orig-head, but interactive rebase used to write
>> +* to head. Fall back to reading from head to cover for the case that
>> +* the user upgraded git with an ongoing interactive rebase.
>> +*/
>> +   filename = state_file_exists(dir, "orig-head") ? "orig-head" : 
>> "head";
>> +   if (read_state_file(, dir, filename) < 0)
>> +   return -1;
>
> So from here on we always use "orig-head" instead of "head" for
> interactive rebase.
> Would people ever rely on the (internal) file name and have e.g.
> scripts which operate
> on the "head" file ?

This backwards-compatibility code is just a straight port from the
code in git-rebase.sh.

The usage of orig-head has been around since 2011 with 84df456
(rebase: extract code for writing basic state, 2011-02-06), so I guess
if people had issues with it, it would have been reported.

>
>
>> +   strbuf_trim();
>> +   if (get_oid_hex(sb.buf, >orig_head) < 0)
>> +   return error(_("could not parse %s"), mkpath("%s/%s", dir, 
>> filename));
>> +
>> +   strbuf_release();
>> +   return 0;
>> +}
>> +
>> +static int write_state_text(const char *dir, const char *file, const char 
>> *string)
>> +{
>> +   return write_file(mkpath("%s/%s&quo

Re: [PATCH/RFC/GSoC 00/17] A barebones git-rebase in C

2016-03-19 Thread Paul Tan
On Tue, Mar 15, 2016 at 2:43 AM, Junio C Hamano <gits...@pobox.com> wrote:
> Duy Nguyen <pclo...@gmail.com> writes:
>
>> On Sat, Mar 12, 2016 at 5:46 PM, Paul Tan <pyoka...@gmail.com> wrote:
>>> So, we have around a 1.4x-1.8x speedup for Linux users, and a 1.7x-13x 
>>> speedup
>>> for Windows users. The annoying long delay before the interactive editor is
>>> launched on Windows is gotten rid of, which I'm very happy about :-)
>>
>> Nice numbers :-) Sorry I can't look at your patches yet. Just a very
>> minor comment from diffstat..
>>
>>>  rebase-am.c| 110 +++
>>>  rebase-am.h|  22 +++
>>>  rebase-common.c| 220 ++
>>>  rebase-common.h|  48 +
>>>  rebase-interactive.c   | 375 
>>> +
>>>  rebase-interactive.h   |  33 
>>>  rebase-merge.c | 256 +
>>>  rebase-merge.h |  28 +++
>>>  rebase-todo.c  | 251 +
>>>  rebase-todo.h  |  55 ++
>>
>> topdir is already very crowded. Maybe you could move all these files
>> to "rebase" subdir.
>
> I think that makes sense.  I do not expect people depending on being
> able to say "git rebase--am" and have it do something useful, so
> they won't belong to builtin/, but rebase/{am,common,...}.[ch] makes
> sense.

Sure, I'll do that.

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 07/17] rebase-common: implement refresh_and_write_cache()

2016-03-19 Thread Paul Tan
On Tue, Mar 15, 2016 at 5:10 AM, Junio C Hamano <gits...@pobox.com> wrote:
> Paul Tan <pyoka...@gmail.com> writes:
>
>> In the upcoming git-rebase to C rewrite, it is a common operation to
>> refresh the index and write the resulting index.
>>
>> builtin/am.c already implements refresh_and_write_cache(), which is what
>> we want. Move it to rebase-common.c, so that it can be shared with all
>> the rebase backends, including git-am.
>
> Your rebase-am might be one of the rebase backends, but git-am is
> not, so it is misleading to count it among "all the rebase
> backends".
>
> I would think that a better home for refresh_and_write_index() is
> right next to write_locked_index(), with #define in cache.h for
> refresh_and_write_cache(), just like others.

Okay, thanks for suggesting a better location.

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 16/17] editor: implement git_sequence_editor() and launch_sequence_editor()

2016-03-19 Thread Paul Tan
Hi Dscho,

On Tue, Mar 15, 2016 at 3:00 PM, Johannes Schindelin
<johannes.schinde...@gmx.de> wrote:
> On Sat, 12 Mar 2016, Paul Tan wrote:
>> ---
>>  cache.h  |  1 +
>
> No need to clutter cache.h with a function that is only to be used by the
> sequencer. IOW let's make this static in sequencer.c.

The function needs to be implemented in editor.c because it would be
better for it to share the same code that launch_editor() uses
(implemented in this patch by splitting the logic into a static
function launch_specific_editor())

We could move the declaration to sequencer.h though.

> I would also prefer pairing this short function with the change that
> actually uses it (in my topic branches, I like to compile with -Werror,
> which would result in a failure due to an unused function), in the same
> patch.

Sure.

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



Re: [PATCH/RFC/GSoC 12/17] rebase-todo: introduce rebase_todo_item

2016-03-19 Thread Paul Tan
Hi Christian,

On Mon, Mar 14, 2016 at 9:43 PM, Christian Couder
<christian.cou...@gmail.com> wrote:
> On Sat, Mar 12, 2016 at 11:46 AM, Paul Tan <pyoka...@gmail.com> wrote:
>> In an interactive rebase, commands are read and executed from a todo
>> list (.git/rebase-merge/git-rebase-todo) to perform the rebase.
>>
>> In the upcoming re-implementation of git-rebase -i in C, it is useful to
>> be able to parse each command into a data structure which can then be
>> operated on. Implement rebase_todo_item for this.
>
> sequencer.{c,h} already has some code to parse and create todo lists
> for cherry-picking or reverting multiple commits, so I am wondering if
> it would be possible to share some code?

AFAIK, sequencer.c as it is in master parses the todo list
destructively and does not keep the associated action for each commit
and the "rest" string. interactive rebase does keep those, so I needed
a different data structure rather than the one currently being used in
sequencer.c.

As I said in another thread, originally I wanted to keep the scope
simple, and just do the rewrite of rebase from C to shell, and let any
further libifications and optimizations come after, so I didn't want
to touch sequencer for now. However, it turns out that Dscho has grand
plans for the unification of sequencer and interactive rebase, so I'll
go with that :-)

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 01/17] perf: introduce performance tests for git-rebase

2016-03-19 Thread Paul Tan
Hi Dscho,

On Wed, Mar 16, 2016 at 3:58 PM, Johannes Schindelin
<johannes.schinde...@gmx.de> wrote:
> Hi Paul,
>
> On Sat, 12 Mar 2016, Paul Tan wrote:
>
>> diff --git a/t/perf/p3404-rebase-interactive.sh 
>> b/t/perf/p3404-rebase-interactive.sh
>> new file mode 100755
>> index 000..aaca105
>> --- /dev/null
>> +++ b/t/perf/p3404-rebase-interactive.sh
>> @@ -0,0 +1,26 @@
>>
>> [...]
>>
>> +test_perf 'rebase -i --onto master^' '
>> + git checkout perf-topic-branch &&
>> + git reset --hard perf-topic-branch-initial &&
>> + GIT_SEQUENCE_EDITOR=: git rebase -i --onto master^ master
>> +'
>
> This measures the performance of checkout && reset && rebase -i. Maybe we
> should only test rebase -i?

test_perf runs the same script multiple times, so we need to reset
--hard at least to undo the changes of the rebase.

I think we can remove the reset if we use rebase -f and rebase onto
the same base, but -f was not implemented in this patch series.

> Also, I would strongly recommend an extra test_commit after reset;
> Otherwise you would only test the logic that verifies that it can simply
> fast-forward instead of cherry-picking.

Or, we could use the -f flag, I think.

Thanks,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 05/17] rebase-options: implement rebase_options_load() and rebase_options_save()

2016-03-19 Thread Paul Tan
Hi Dscho,

On Wed, Mar 16, 2016 at 4:04 PM, Johannes Schindelin
 wrote:
> In addition I want to point out that sequencer's replay_opts seem to be at
> least related, but the patch shares none of its code with the sequencer.
> Let's avoid that.
>
> In other words, let's try to add as little code as possible when we can
> enhance existing code.

Well, both git-rebase--am.sh and git-rebase--merge.sh do not use the
sequencer functionality at all, and we don't see git-am for example
needing to be aware of onto, orig-head, head-name etc.

Besides, I don't see why the sequencer needs to be aware of these
rebase-specific options. For simplicity[1], I would think the
sequencer would only need to be aware of what the todo list is, since
that is common to cherry-pick/revert and rebase-i, and all the other
non-sequencer related stuff like checking out the --onto ,
updating refs can be done at the rebase-interactive.c or
git-rebase--interactive.sh layer.

[1] Of course, it's kind of unfortunate that sequencer.c has to be
aware of whether it is being called as cherry-pick or revert, but I
don't see why implementing interactive rebase functionality needs to
make the same mistake.

Thanks,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFC/GSoC 17/17] rebase-interactive: introduce interactive backend for builtin rebase

2016-03-15 Thread Paul Tan
Hi Dscho,

On Tue, Mar 15, 2016 at 3:57 PM, Johannes Schindelin
<johannes.schinde...@gmx.de> wrote:
> On Sat, 12 Mar 2016, Paul Tan wrote:
>
>> Since 1b1dce4 (Teach rebase an interactive mode, 2007-06-25), git-rebase
>> supports an interactive mode when passed the -i switch.
>>
>> In interactive mode, git-rebase allows users to edit the list of patches
>> (using the user's GIT_SEQUENCE_EDITOR), so that the user can reorder,
>> edit and delete patches.
>>
>> Re-implement a skeletal version of the above feature by introducing a
>> rebase-interactive backend for our builtin-rebase. This skeletal
>> implementation is only able to pick and re-order commits.
>>
>> Signed-off-by: Paul Tan <pyoka...@gmail.com>
>
> It is a pity that both of us worked on overlapping projects in stealth
> mode. Inevitably, some of the work is now wasted :-(

No worries, I did this series for my own interest, especially to get a
gauge of the speedup between rebase in shell and C. GSoC applications
have opened and will close in 10 days time, so I wanted to get some
data before the deadline at least :-).

> Not all is lost, though.
>
> Much of the code can be salvaged, although I really want to reiterate
> that an all-or-nothing conversion of the rebase command is not going to
> fly.

Sure. I admit that I concentrated more on how the "final code" would
look like, and not so much how the rewrite would be built upon in
pieces.

> For several reasons: it would be rather disruptive, huge and hard to
> review. It would not let anybody else work on that huge task. And you're
> prone to fall behind due to Git's source code being in constant flux
> (including the rebase bits).
>
> There is another, really important reason: if you package the conversion
> into small, neat bundles, it is much easier to avoid too narrow a focus
> that would tuck perfectly useful functions away in a location where it
> cannot be reused and where it is likely to be missed by other developers
> who need the same, or similar functionality (point in case:
> has_uncommitted_changes()). And we know that this happened in the past,
> and sometimes resulted in near-duplicated code, hence Karthik's Herculean,
> still ongoing work.
>
> Lastly, I need to point out that the conversion of rebase into a builtin
> is not the end game, it is the game's opening.
>
> [...]
>
> So you see, there was a much larger master plan behind my recommendation
> to go the rebase--helper route.

Ah I see, thanks for publishing your branch and sharing your plans.

Originally I was thinking smaller -- rewrite git-rebase first,
following its shell script closely, and then doing the libification
and optimization after that. However, I see now that you have grander
plans than that :-).

>
> As to my current state: Junio put me into quite a fix (without knowing it)
> by releasing 2.7.3 just after I took off for an extended offline weekend,
> and now I am scrambling because a change in MSYS2's runtime (actually,
> probably more like: an update of the GCC that is used to compile the
> runtime, that now causes a regression) is keeping me away from my work on
> the interactive rebase. Even so, I am pretty far along; There are only
> three major things left to do: 1) fix fixups/squashes with fast-forwarding
> picks, 2) implement 'reword', 3) display the progress.  And of course 4)
> clean up the fallout. ;-)
>
> At this point, I'd rather finish this myself than falling prey to Brooks'
> Law.

Okay, I won't touch interactive rebase then.

> I also have to admit that I would love to give you a project over the
> summer whose logical children are exciting enough to dabble with even
> during the winter. And somehow I do not see that excitement in the boring
> conversion from shell to C (even if its outcome is well-needed).

Well, that is subjective ;-).

Even with interactive rebase out-of-bounds, I don't think it's a dead
end though:

1. git-rebase--am.sh, git-rebase--merge.sh and git-rebase.sh can be
rewritten to C, and call git-rebase--interactive.sh externally, like
what Duy demonstrated in his patch series. The timings show that am
and merge rebase still benefit, and that way we will be closer to a
git-rebase in full C.

2. git-commit can be libified, so that we can access its functionality
directly. (sequencer.c runs it once per commit, rebase-interactive
uses it for squashes etc.)

Or would that be stepping on your toes?

> Ciao,
> Dscho
>
> Footnote *1*:
> https://github.com/git-for-windows/build-extra/blob/master/shears.sh

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 16/17] editor: implement git_sequence_editor() and launch_sequence_editor()

2016-03-12 Thread Paul Tan
Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 cache.h  |  1 +
 editor.c | 27 +--
 strbuf.h |  1 +
 3 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/cache.h b/cache.h
index aa5e97c..d7a6fc6 100644
--- a/cache.h
+++ b/cache.h
@@ -1222,6 +1222,7 @@ extern const char *fmt_name(const char *name, const char 
*email);
 extern const char *ident_default_name(void);
 extern const char *ident_default_email(void);
 extern const char *git_editor(void);
+extern const char *git_sequence_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int git_ident_config(const char *, const char *, void *);
 
diff --git a/editor.c b/editor.c
index 01c644c..4c5874b 100644
--- a/editor.c
+++ b/editor.c
@@ -29,10 +29,22 @@ const char *git_editor(void)
return editor;
 }
 
-int launch_editor(const char *path, struct strbuf *buffer, const char *const 
*env)
+const char *git_sequence_editor(void)
 {
-   const char *editor = git_editor();
+   const char *sequence_editor = getenv("GIT_SEQUENCE_EDITOR");
+
+   if (sequence_editor && *sequence_editor)
+   return sequence_editor;
 
+   git_config_get_string_const("sequence.editor", _editor);
+   if (sequence_editor && *sequence_editor)
+   return sequence_editor;
+
+   return git_editor();
+}
+
+static int launch_specific_editor(const char *editor, const char *path, struct 
strbuf *buffer, const char *const *env)
+{
if (!editor)
return error("Terminal is dumb, but EDITOR unset");
 
@@ -65,5 +77,16 @@ int launch_editor(const char *path, struct strbuf *buffer, 
const char *const *en
if (strbuf_read_file(buffer, path, 0) < 0)
return error("could not read file '%s': %s",
path, strerror(errno));
+
return 0;
 }
+
+int launch_editor(const char *path, struct strbuf *buffer, const char *const 
*env)
+{
+   return launch_specific_editor(git_editor(), path, buffer, env);
+}
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer, const char 
*const *env)
+{
+   return launch_specific_editor(git_sequence_editor(), path, buffer, env);
+}
diff --git a/strbuf.h b/strbuf.h
index f72fd14..aebdcd7 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -524,6 +524,7 @@ extern void strbuf_add_unique_abbrev(struct strbuf *sb,
  * file's contents are not read into the buffer upon completion.
  */
 extern int launch_editor(const char *path, struct strbuf *buffer, const char 
*const *env);
+extern int launch_sequence_editor(const char *path, struct strbuf *buffer, 
const char *const *env);
 
 extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char 
*buf, size_t size);
 
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 13/17] rebase-todo: introduce rebase_todo_list

2016-03-12 Thread Paul Tan
Implement rebase_todo_list, which is a resizable array of
rebase_todo_items.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 rebase-todo.c | 107 ++
 rebase-todo.h |  27 +++
 2 files changed, 134 insertions(+)

diff --git a/rebase-todo.c b/rebase-todo.c
index ac6b222..4f14638 100644
--- a/rebase-todo.c
+++ b/rebase-todo.c
@@ -142,3 +142,110 @@ void strbuf_add_rebase_todo_item(struct strbuf *sb,
 
strbuf_addch(sb, '\n');
 }
+
+void rebase_todo_list_init(struct rebase_todo_list *list)
+{
+   list->items = NULL;
+   list->nr = 0;
+   list->alloc = 0;
+}
+
+void rebase_todo_list_clear(struct rebase_todo_list *list)
+{
+   unsigned int i;
+
+   for (i = 0; i < list->nr; i++)
+   rebase_todo_item_release(>items[i]);
+   free(list->items);
+   rebase_todo_list_init(list);
+}
+
+void rebase_todo_list_swap(struct rebase_todo_list *dst,
+  struct rebase_todo_list *src)
+{
+   struct rebase_todo_list tmp = *dst;
+
+   *dst = *src;
+   *src = tmp;
+}
+
+unsigned int rebase_todo_list_count(const struct rebase_todo_list *list)
+{
+   unsigned int i, count = 0;
+
+   for (i = 0; i < list->nr; i++)
+   if (list->items[i].action != REBASE_TODO_NONE)
+   count++;
+   return count;
+}
+
+struct rebase_todo_item *rebase_todo_list_push(struct rebase_todo_list *list, 
const struct rebase_todo_item *src_item)
+{
+   struct rebase_todo_item *item = rebase_todo_list_push_empty(list);
+
+   rebase_todo_item_copy(item, src_item);
+   return item;
+}
+
+struct rebase_todo_item *rebase_todo_list_push_empty(struct rebase_todo_list 
*list)
+{
+   struct rebase_todo_item *item;
+
+   ALLOC_GROW(list->items, list->nr + 1, list->alloc);
+   item = >items[list->nr++];
+   rebase_todo_item_init(item);
+   return item;
+}
+
+struct rebase_todo_item *rebase_todo_list_push_noop(struct rebase_todo_list 
*list)
+{
+   struct rebase_todo_item *item = rebase_todo_list_push_empty(list);
+
+   item->action = REBASE_TODO_NOOP;
+   return item;
+}
+
+int rebase_todo_list_load(struct rebase_todo_list *list, const char *path, int 
abbrev)
+{
+   struct strbuf sb = STRBUF_INIT;
+   FILE *fp;
+
+   fp = fopen(path, "r");
+   if (!fp)
+   return error(_("could not open %s for reading"), path);
+
+   while (strbuf_getline(, fp) != EOF) {
+   struct rebase_todo_item *item = 
rebase_todo_list_push_empty(list);
+   if (rebase_todo_item_parse(item, sb.buf, abbrev) < 0) {
+   rebase_todo_item_release(item);
+   list->nr--;
+   strbuf_release();
+   fclose(fp);
+   return -1;
+   }
+   }
+   strbuf_release();
+   fclose(fp);
+   return 0;
+}
+
+void rebase_todo_list_save(const struct rebase_todo_list *list, const char 
*filename, unsigned int offset, int abbrev)
+{
+   char *tmpfile = mkpathdup("%s.new", filename);
+   struct strbuf sb = STRBUF_INIT;
+   int fd;
+
+   for (; offset < list->nr; offset++)
+   strbuf_add_rebase_todo_item(, >items[offset], abbrev);
+
+   fd = xopen(tmpfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+   if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+   die_errno(_("could not write to %s"), tmpfile);
+   close(fd);
+   strbuf_release();
+
+   if (rename(tmpfile, filename))
+   die_errno(_("rename failed"));
+
+   free(tmpfile);
+}
diff --git a/rebase-todo.h b/rebase-todo.h
index 2eedbb0..f602fd2 100644
--- a/rebase-todo.h
+++ b/rebase-todo.h
@@ -25,4 +25,31 @@ int rebase_todo_item_parse(struct rebase_todo_item *, const 
char *line, int abbr
 
 void strbuf_add_rebase_todo_item(struct strbuf *, const struct 
rebase_todo_item *, int abbrev);
 
+struct rebase_todo_list {
+   struct rebase_todo_item *items;
+   unsigned int nr, alloc;
+};
+
+#define REBASE_TODO_LIST_INIT { NULL, 0, 0 }
+
+void rebase_todo_list_init(struct rebase_todo_list *);
+
+void rebase_todo_list_clear(struct rebase_todo_list *);
+
+void rebase_todo_list_swap(struct rebase_todo_list *dst, struct 
rebase_todo_list *src);
+
+unsigned int rebase_todo_list_count(const struct rebase_todo_list *);
+
+struct rebase_todo_item *rebase_todo_list_push(struct rebase_todo_list *,
+  const struct rebase_todo_item *);
+
+struct rebase_todo_item *rebase_todo_list_push_empty(struct rebase_todo_list 
*);
+
+struct rebase_todo_item *rebase_todo_list_push_noop(struct rebase_todo_list *);
+
+int rebase_todo_list_load(struct rebase_todo_list *, const char *path, int 
abbrev);
+
+void rebase_todo_list_save(const struct rebase

[PATCH/RFC/GSoC 08/17] rebase-common: let refresh_and_write_cache() take a flags argument

2016-03-12 Thread Paul Tan
refresh_and_write_cache() is a handy function for refreshing the index
and writing the resulting index back to the filesystem. However, it
always calls refresh_cache() with REFRESH_QUIET. Allow callers to modify
the behavior of refresh_cache() by allowing callers to pass a flags
argument to refresh_cache().

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 builtin/am.c| 2 +-
 rebase-common.c | 4 ++--
 rebase-common.h | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 504b604..5185719 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1815,7 +1815,7 @@ static void am_run(struct am_state *state, int resume)
 
unlink(am_path(state, "dirtyindex"));
 
-   refresh_and_write_cache();
+   refresh_and_write_cache(REFRESH_QUIET);
 
if (index_has_changes()) {
write_state_bool(state, "dirtyindex", 1);
diff --git a/rebase-common.c b/rebase-common.c
index b07e1f1..97b0687 100644
--- a/rebase-common.c
+++ b/rebase-common.c
@@ -5,12 +5,12 @@
 #include "refs.h"
 #include "lockfile.h"
 
-void refresh_and_write_cache(void)
+void refresh_and_write_cache(unsigned int flags)
 {
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
hold_locked_index(lock_file, 1);
-   refresh_cache(REFRESH_QUIET);
+   refresh_cache(flags);
if (write_locked_index(_index, lock_file, COMMIT_LOCK))
die(_("unable to write index file"));
 }
diff --git a/rebase-common.h b/rebase-common.h
index 8620e8c..4586f03 100644
--- a/rebase-common.h
+++ b/rebase-common.h
@@ -4,7 +4,7 @@
 /**
  * Refresh and write index.
  */
-void refresh_and_write_cache(void);
+void refresh_and_write_cache(unsigned int);
 
 /* common rebase backend options */
 struct rebase_options {
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 12/17] rebase-todo: introduce rebase_todo_item

2016-03-12 Thread Paul Tan
In an interactive rebase, commands are read and executed from a todo
list (.git/rebase-merge/git-rebase-todo) to perform the rebase.

In the upcoming re-implementation of git-rebase -i in C, it is useful to
be able to parse each command into a data structure which can then be
operated on. Implement rebase_todo_item for this.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 Makefile  |   1 +
 rebase-todo.c | 144 ++
 rebase-todo.h |  28 
 3 files changed, 173 insertions(+)
 create mode 100644 rebase-todo.c
 create mode 100644 rebase-todo.h

diff --git a/Makefile b/Makefile
index d43e068..8b928e4 100644
--- a/Makefile
+++ b/Makefile
@@ -782,6 +782,7 @@ LIB_OBJS += read-cache.o
 LIB_OBJS += rebase-am.o
 LIB_OBJS += rebase-common.o
 LIB_OBJS += rebase-merge.o
+LIB_OBJS += rebase-todo.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
diff --git a/rebase-todo.c b/rebase-todo.c
new file mode 100644
index 000..ac6b222
--- /dev/null
+++ b/rebase-todo.c
@@ -0,0 +1,144 @@
+#include "cache.h"
+#include "rebase-todo.h"
+
+/*
+ * Used as the default `rest` value, so that users can always assume `rest` is
+ * non NULL and `rest` is NUL terminated even for a freshly initialized
+ * rebase_todo_item.
+ */
+static char rebase_todo_item_slopbuf[1];
+
+void rebase_todo_item_init(struct rebase_todo_item *item)
+{
+   item->action = REBASE_TODO_NONE;
+   oidclr(>oid);
+   item->rest = rebase_todo_item_slopbuf;
+}
+
+void rebase_todo_item_release(struct rebase_todo_item *item)
+{
+   if (item->rest != rebase_todo_item_slopbuf)
+   free(item->rest);
+   rebase_todo_item_init(item);
+}
+
+void rebase_todo_item_copy(struct rebase_todo_item *dst, const struct 
rebase_todo_item *src)
+{
+   if (dst->rest != rebase_todo_item_slopbuf)
+   free(dst->rest);
+   *dst = *src;
+   dst->rest = xstrdup(src->rest);
+}
+
+static const char *next_word(struct strbuf *sb, const char *str)
+{
+   const char *end;
+
+   while (*str && isspace(*str))
+   str++;
+
+   end = str;
+   while (*end && !isspace(*end))
+   end++;
+
+   strbuf_reset(sb);
+   strbuf_add(sb, str, end - str);
+   return end;
+}
+
+int rebase_todo_item_parse(struct rebase_todo_item *item, const char *line, 
int abbrev)
+{
+   struct strbuf word = STRBUF_INIT;
+   const char *str = line;
+   int has_oid = 1, ret = 0;
+
+   while (*str && isspace(*str))
+   str++;
+
+   if (!*str || *str == comment_line_char) {
+   item->action = REBASE_TODO_NONE;
+   oidclr(>oid);
+   if (item->rest != rebase_todo_item_slopbuf)
+   free(item->rest);
+   item->rest = *str ? xstrdup(str) : rebase_todo_item_slopbuf;
+   return 0;
+   }
+
+   str = next_word(, str);
+   if (!strcmp(word.buf, "noop")) {
+   item->action = REBASE_TODO_NOOP;
+   has_oid = 0;
+   } else if (!strcmp(word.buf, "pick") || !strcmp(word.buf, "p")) {
+   item->action = REBASE_TODO_PICK;
+   } else {
+   ret = error(_("Unknown command: %s"), word.buf);
+   goto finish;
+   }
+
+   if (has_oid) {
+   str = next_word(, str);
+   if (abbrev) {
+   /* accept abbreviated object ids */
+   if (get_oid_commit(word.buf, >oid)) {
+   ret = error(_("Not a commit: %s"), word.buf);
+   goto finish;
+   }
+   } else {
+   if (word.len != GIT_SHA1_HEXSZ || get_oid_hex(word.buf, 
>oid)) {
+   ret = error(_("Invalid line: %s"), line);
+   goto finish;
+   }
+   }
+   } else {
+   oidclr(>oid);
+   }
+
+   if (*str && isspace(*str))
+   str++;
+   if (*str) {
+   if (item->rest != rebase_todo_item_slopbuf)
+   free(item->rest);
+   item->rest = xstrdup(str);
+   }
+
+finish:
+   strbuf_release();
+   return ret;
+}
+
+void strbuf_add_rebase_todo_item(struct strbuf *sb,
+const struct rebase_todo_item *item, int 
abbrev)
+{
+   int has_oid = 1;
+
+   switch (item->action) {
+   case REBASE_TODO_NONE:
+   has_oid = 0;
+   break;
+   case REBASE_TODO_NOOP:
+   strbuf_addstr(sb, "noop");
+   has_oid = 0;
+   break;
+   case REBASE_TODO_PICK:
+   strbuf_addstr(

[PATCH/RFC/GSoC 10/17] rebase-common: implement cache_has_uncommitted_changes()

2016-03-12 Thread Paul Tan
In the upcoming git-rebase-to-C rewrite, it is a common opertation to
check if the index has uncommitted changes, so that rebase can complain
that the index is dirty, or commit the uncommitted changes in the index.

builtin/pull.c already implements the function we want. Move it to
rebase-common.c so that it can be shared between all rebase backends and
git-pull.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 builtin/pull.c  | 22 +-
 rebase-common.c | 17 +
 rebase-common.h |  5 +
 3 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/builtin/pull.c b/builtin/pull.c
index 9e65dc9..6be4213 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -307,26 +307,6 @@ static enum rebase_type config_get_rebase(void)
 }
 
 /**
- * Returns 1 if there are uncommitted changes, 0 otherwise.
- */
-static int has_uncommitted_changes(const char *prefix)
-{
-   struct rev_info rev_info;
-   int result;
-
-   if (is_cache_unborn())
-   return 0;
-
-   init_revisions(_info, prefix);
-   DIFF_OPT_SET(_info.diffopt, IGNORE_SUBMODULES);
-   DIFF_OPT_SET(_info.diffopt, QUICK);
-   add_head_to_pending(_info);
-   diff_setup_done(_info.diffopt);
-   result = run_diff_index(_info, 1);
-   return diff_result_code(_info.diffopt, result);
-}
-
-/**
  * If the work tree has unstaged or uncommitted changes, dies with the
  * appropriate message.
  */
@@ -345,7 +325,7 @@ static void die_on_unclean_work_tree(const char *prefix)
do_die = 1;
}
 
-   if (has_uncommitted_changes(prefix)) {
+   if (cache_has_uncommitted_changes()) {
if (do_die)
error(_("Additionally, your index contains uncommitted 
changes."));
else
diff --git a/rebase-common.c b/rebase-common.c
index 61be8f1..94783a9 100644
--- a/rebase-common.c
+++ b/rebase-common.c
@@ -29,6 +29,23 @@ int cache_has_unstaged_changes(void)
return diff_result_code(_info.diffopt, result);
 }
 
+int cache_has_uncommitted_changes(void)
+{
+   struct rev_info rev_info;
+   int result;
+
+   if (is_cache_unborn())
+   return 0;
+
+   init_revisions(_info, NULL);
+   DIFF_OPT_SET(_info.diffopt, IGNORE_SUBMODULES);
+   DIFF_OPT_SET(_info.diffopt, QUICK);
+   add_head_to_pending(_info);
+   diff_setup_done(_info.diffopt);
+   result = run_diff_index(_info, 1);
+   return diff_result_code(_info.diffopt, result);
+}
+
 void rebase_options_init(struct rebase_options *opts)
 {
oidclr(>onto);
diff --git a/rebase-common.h b/rebase-common.h
index 9d14e25..97d9a5b 100644
--- a/rebase-common.h
+++ b/rebase-common.h
@@ -11,6 +11,11 @@ void refresh_and_write_cache(unsigned int);
  */
 int cache_has_unstaged_changes(void);
 
+/**
+ * Returns 1 if there are uncommitted changes, 0 otherwise.
+ */
+int cache_has_uncommitted_changes(void);
+
 /* common rebase backend options */
 struct rebase_options {
struct object_id onto;
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 14/17] status: use rebase_todo_list

2016-03-12 Thread Paul Tan
Since 84e6fb9 (status: give more information during rebase -i,
2015-07-06), git status during an interactive rebase will show the list
of commands that are done and yet to be done. It implemented its own
hand-rolled parser in order to achieve this.

Now that we are able to fully parse interactive rebase's todo lists with
rebase_todo_list_parse(), use it in wt-status.c to reduce the amount of
code needed to implement this feature.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---

This patch is just an illustration, and is not quite right as it does not strip
comments and blank lines like the original did.

 wt-status.c | 100 +++-
 1 file changed, 25 insertions(+), 75 deletions(-)

diff --git a/wt-status.c b/wt-status.c
index ab4f80d..96b82ef 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,6 +15,7 @@
 #include "column.h"
 #include "strbuf.h"
 #include "utf8.h"
+#include "rebase-todo.h"
 
 static const char cut_line[] =
 " >8 \n";
@@ -1026,94 +1027,39 @@ static int split_commit_in_progress(struct wt_status *s)
return split_in_progress;
 }
 
-/*
- * Turn
- * "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message"
- * into
- * "pick d6a2f03 some message"
- *
- * The function assumes that the line does not contain useless spaces
- * before or after the command.
- */
-static void abbrev_sha1_in_line(struct strbuf *line)
-{
-   struct strbuf **split;
-   int i;
-
-   if (starts_with(line->buf, "exec ") ||
-   starts_with(line->buf, "x "))
-   return;
-
-   split = strbuf_split_max(line, ' ', 3);
-   if (split[0] && split[1]) {
-   unsigned char sha1[20];
-   const char *abbrev;
-
-   /*
-* strbuf_split_max left a space. Trim it and re-add
-* it after abbreviation.
-*/
-   strbuf_trim(split[1]);
-   if (!get_sha1(split[1]->buf, sha1)) {
-   abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
-   strbuf_reset(split[1]);
-   strbuf_addf(split[1], "%s ", abbrev);
-   strbuf_reset(line);
-   for (i = 0; split[i]; i++)
-   strbuf_addf(line, "%s", split[i]->buf);
-   }
-   }
-   for (i = 0; split[i]; i++)
-   strbuf_release(split[i]);
-
-}
-
-static void read_rebase_todolist(const char *fname, struct string_list *lines)
-{
-   struct strbuf line = STRBUF_INIT;
-   FILE *f = fopen(git_path("%s", fname), "r");
-
-   if (!f)
-   die_errno("Could not open file %s for reading",
- git_path("%s", fname));
-   while (!strbuf_getline_lf(, f)) {
-   if (line.len && line.buf[0] == comment_line_char)
-   continue;
-   strbuf_trim();
-   if (!line.len)
-   continue;
-   abbrev_sha1_in_line();
-   string_list_append(lines, line.buf);
-   }
-}
-
 static void show_rebase_information(struct wt_status *s,
struct wt_status_state *state,
const char *color)
 {
if (state->rebase_interactive_in_progress) {
-   int i;
-   int nr_lines_to_show = 2;
+   unsigned int i;
+   unsigned int nr_lines_to_show = 2;
+   struct strbuf sb = STRBUF_INIT;
 
-   struct string_list have_done = STRING_LIST_INIT_DUP;
-   struct string_list yet_to_do = STRING_LIST_INIT_DUP;
+   struct rebase_todo_list have_done = REBASE_TODO_LIST_INIT;
+   struct rebase_todo_list yet_to_do = REBASE_TODO_LIST_INIT;
 
-   read_rebase_todolist("rebase-merge/done", _done);
-   read_rebase_todolist("rebase-merge/git-rebase-todo", 
_to_do);
+   if (rebase_todo_list_load(_done, 
git_path("rebase-merge/done"), 1) < 0)
+   return;
+   if (rebase_todo_list_load(_to_do, 
git_path("rebase-merge/git-rebase-todo"), 1) < 0)
+   return;
 
if (have_done.nr == 0)
status_printf_ln(s, color, _("No commands done."));
else {
status_printf_ln(s, color,
-   Q_("Last command done (%d command done):",
-   "Last commands done (%d commands 
done):",
+   Q_("Last command done (%u command done):",
+ 

[PATCH/RFC/GSoC 06/17] rebase-am: introduce am backend for builtin rebase

2016-03-12 Thread Paul Tan
Since 7f59dbb (Rewrite rebase to use git-format-patch piped to git-am.,
2005-11-14), git-rebase will by default use "git am" to rebase commits.
This is done by first checking out to the new base commit, generating a
series of patches with the commits to replay, and then applying them
with git-am. Finally, if orig_head is a branch, it is updated to point
to the tip of the new rebased commit history.

Implement a skeletal version of this method of rebasing commits by
introducing a new rebase-am backend for our builtin-rebase. This
skeletal version can only call git-format-patch and git-am to perform a
rebase, and is unable to resume from a failed rebase. Subsequent
patches will re-implement all the missing features.

The symmetric difference between upstream...orig_head is used because in
a later patch, we will add an additional exclusion revision in order to
handle fork points correctly.  See b6266dc (rebase--am: use
--cherry-pick instead of --ignore-if-in-upstream, 2014-07-15).

The initial steps of checking out the new base commit, and the final
cleanup steps of updating refs are common between the am backend and
merge backend. As such, we implement the common setup and teardown
sequence in the shared functions rebase_common_setup() and
rebase_common_finish(), so we can share code with the merge backend when
it is implemented in a later patch.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 Makefile |   1 +
 builtin/rebase.c |  25 +
 rebase-am.c  | 110 +++
 rebase-am.h  |  22 +++
 rebase-common.c  |  81 
 rebase-common.h  |   6 +++
 6 files changed, 245 insertions(+)
 create mode 100644 rebase-am.c
 create mode 100644 rebase-am.h

diff --git a/Makefile b/Makefile
index b29c672..a2618ea 100644
--- a/Makefile
+++ b/Makefile
@@ -779,6 +779,7 @@ LIB_OBJS += prompt.o
 LIB_OBJS += quote.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
+LIB_OBJS += rebase-am.o
 LIB_OBJS += rebase-common.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 40176ca..ec63d3b 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -8,6 +8,22 @@
 #include "remote.h"
 #include "branch.h"
 #include "refs.h"
+#include "rebase-am.h"
+
+enum rebase_type {
+   REBASE_TYPE_NONE = 0,
+   REBASE_TYPE_AM
+};
+
+static const char *rebase_dir(enum rebase_type type)
+{
+   switch (type) {
+   case REBASE_TYPE_AM:
+   return git_path_rebase_am_dir();
+   default:
+   die("BUG: invalid rebase_type %d", type);
+   }
+}
 
 /**
  * Used by get_curr_branch_upstream_name() as a for_each_remote() callback to
@@ -208,6 +224,15 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("Failed to resolve '%s' as a valid revision."), 
"HEAD");
}
 
+   /* Run the appropriate rebase backend */
+   {
+   struct rebase_am state;
+   rebase_am_init(, rebase_dir(REBASE_TYPE_AM));
+   rebase_options_swap(, _opts);
+   rebase_am_run();
+   rebase_am_release();
+   }
+
rebase_options_release(_opts);
return 0;
 }
diff --git a/rebase-am.c b/rebase-am.c
new file mode 100644
index 000..53e8798
--- /dev/null
+++ b/rebase-am.c
@@ -0,0 +1,110 @@
+#include "cache.h"
+#include "rebase-am.h"
+#include "run-command.h"
+
+GIT_PATH_FUNC(git_path_rebase_am_dir, "rebase-apply");
+
+void rebase_am_init(struct rebase_am *state, const char *dir)
+{
+   if (!dir)
+   dir = git_path_rebase_am_dir();
+   rebase_options_init(>opts);
+   state->dir = xstrdup(dir);
+}
+
+void rebase_am_release(struct rebase_am *state)
+{
+   rebase_options_release(>opts);
+   free(state->dir);
+}
+
+int rebase_am_in_progress(const struct rebase_am *state)
+{
+   const char *dir = state ? state->dir : git_path_rebase_am_dir();
+   struct stat st;
+
+   return !lstat(dir, ) && S_ISDIR(st.st_mode);
+}
+
+int rebase_am_load(struct rebase_am *state)
+{
+   if (rebase_options_load(>opts, state->dir) < 0)
+   return -1;
+
+   return 0;
+}
+
+static int run_format_patch(const char *patches, const struct object_id *left,
+   const struct object_id *right)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+   int ret;
+
+   cp.git_cmd = 1;
+   cp.out = xopen(patches, O_WRONLY | O_CREAT, 0777);
+   argv_array_push(, "format-patch");
+   argv_array_push(, "-k");
+   argv_array_push(, "--stdout");
+   argv_array_push(, "--full-index");
+   argv_array_push(, "--cherry-pick");
+   argv_array_push(, "--right-only");

[PATCH/RFC/GSoC 09/17] rebase-common: implement cache_has_unstaged_changes()

2016-03-12 Thread Paul Tan
In the upcoming git-rebase-to-C rewrite, it is a common operation to
check if the worktree has unstaged changes, so that it can complain that
the worktree is dirty.

builtin/pull.c already implements this function. Move it to
rebase-common.c so that it can be shared between all rebase backends and
git-pull.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 builtin/pull.c  | 19 ++-
 rebase-common.c | 14 ++
 rebase-common.h |  5 +
 3 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/builtin/pull.c b/builtin/pull.c
index 10eff03..9e65dc9 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -17,6 +17,7 @@
 #include "revision.h"
 #include "tempfile.h"
 #include "lockfile.h"
+#include "rebase-common.h"
 
 enum rebase_type {
REBASE_INVALID = -1,
@@ -306,22 +307,6 @@ static enum rebase_type config_get_rebase(void)
 }
 
 /**
- * Returns 1 if there are unstaged changes, 0 otherwise.
- */
-static int has_unstaged_changes(const char *prefix)
-{
-   struct rev_info rev_info;
-   int result;
-
-   init_revisions(_info, prefix);
-   DIFF_OPT_SET(_info.diffopt, IGNORE_SUBMODULES);
-   DIFF_OPT_SET(_info.diffopt, QUICK);
-   diff_setup_done(_info.diffopt);
-   result = run_diff_files(_info, 0);
-   return diff_result_code(_info.diffopt, result);
-}
-
-/**
  * Returns 1 if there are uncommitted changes, 0 otherwise.
  */
 static int has_uncommitted_changes(const char *prefix)
@@ -355,7 +340,7 @@ static void die_on_unclean_work_tree(const char *prefix)
update_index_if_able(_index, lock_file);
rollback_lock_file(lock_file);
 
-   if (has_unstaged_changes(prefix)) {
+   if (cache_has_unstaged_changes()) {
error(_("Cannot pull with rebase: You have unstaged changes."));
do_die = 1;
}
diff --git a/rebase-common.c b/rebase-common.c
index 97b0687..61be8f1 100644
--- a/rebase-common.c
+++ b/rebase-common.c
@@ -4,6 +4,7 @@
 #include "run-command.h"
 #include "refs.h"
 #include "lockfile.h"
+#include "revision.h"
 
 void refresh_and_write_cache(unsigned int flags)
 {
@@ -15,6 +16,19 @@ void refresh_and_write_cache(unsigned int flags)
die(_("unable to write index file"));
 }
 
+int cache_has_unstaged_changes(void)
+{
+   struct rev_info rev_info;
+   int result;
+
+   init_revisions(_info, NULL);
+   DIFF_OPT_SET(_info.diffopt, IGNORE_SUBMODULES);
+   DIFF_OPT_SET(_info.diffopt, QUICK);
+   diff_setup_done(_info.diffopt);
+   result = run_diff_files(_info, 0);
+   return diff_result_code(_info.diffopt, result);
+}
+
 void rebase_options_init(struct rebase_options *opts)
 {
oidclr(>onto);
diff --git a/rebase-common.h b/rebase-common.h
index 4586f03..9d14e25 100644
--- a/rebase-common.h
+++ b/rebase-common.h
@@ -6,6 +6,11 @@
  */
 void refresh_and_write_cache(unsigned int);
 
+/**
+ * Returns 1 if there are unstaged changes, 0 otherwise.
+ */
+int cache_has_unstaged_changes(void);
+
 /* common rebase backend options */
 struct rebase_options {
struct object_id onto;
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 11/17] rebase-merge: introduce merge backend for builtin rebase

2016-03-12 Thread Paul Tan
Since 58634db (rebase: Allow merge strategies to be used when rebasing,
2006-06-21), git-rebase supported rebasing with a merge strategy when
the -m switch is used.

Re-implement a skeletal version of the above method of rebasing in a new
rebase-merge backend for our builtin-rebase. This skeletal version is
only able to re-apply commits using the merge-recursive strategy, and is
unable to resume from a conflict. Subsequent patches will re-implement
all the missing features.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 Makefile |   1 +
 builtin/rebase.c |  17 +++-
 rebase-merge.c   | 256 +++
 rebase-merge.h   |  28 ++
 4 files changed, 300 insertions(+), 2 deletions(-)
 create mode 100644 rebase-merge.c
 create mode 100644 rebase-merge.h

diff --git a/Makefile b/Makefile
index a2618ea..d43e068 100644
--- a/Makefile
+++ b/Makefile
@@ -781,6 +781,7 @@ LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += rebase-am.o
 LIB_OBJS += rebase-common.o
+LIB_OBJS += rebase-merge.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ec63d3b..6d42115 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -9,10 +9,12 @@
 #include "branch.h"
 #include "refs.h"
 #include "rebase-am.h"
+#include "rebase-merge.h"
 
 enum rebase_type {
REBASE_TYPE_NONE = 0,
-   REBASE_TYPE_AM
+   REBASE_TYPE_AM,
+   REBASE_TYPE_MERGE
 };
 
 static const char *rebase_dir(enum rebase_type type)
@@ -20,6 +22,8 @@ static const char *rebase_dir(enum rebase_type type)
switch (type) {
case REBASE_TYPE_AM:
return git_path_rebase_am_dir();
+   case REBASE_TYPE_MERGE:
+   return git_path_rebase_merge_dir();
default:
die("BUG: invalid rebase_type %d", type);
}
@@ -137,6 +141,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
struct rebase_options rebase_opts;
const char *onto_name = NULL;
const char *branch_name;
+   int do_merge = 0;
 
const char * const usage[] = {
N_("git rebase [options] [--onto ] [] 
[]"),
@@ -146,6 +151,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_GROUP(N_("Available options are")),
OPT_STRING(0, "onto", _name, NULL,
N_("rebase onto given branch instead of upstream")),
+   OPT_BOOL('m', "merge", _merge,
+   N_("use merging strategies to rebase")),
OPT_END()
};
 
@@ -225,7 +232,13 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
 
/* Run the appropriate rebase backend */
-   {
+   if (do_merge) {
+   struct rebase_merge state;
+   rebase_merge_init(, rebase_dir(REBASE_TYPE_MERGE));
+   rebase_options_swap(, _opts);
+   rebase_merge_run();
+   rebase_merge_release();
+   } else {
struct rebase_am state;
rebase_am_init(, rebase_dir(REBASE_TYPE_AM));
rebase_options_swap(, _opts);
diff --git a/rebase-merge.c b/rebase-merge.c
new file mode 100644
index 000..dc96faf
--- /dev/null
+++ b/rebase-merge.c
@@ -0,0 +1,256 @@
+#include "cache.h"
+#include "rebase-merge.h"
+#include "run-command.h"
+#include "dir.h"
+#include "revision.h"
+
+GIT_PATH_FUNC(git_path_rebase_merge_dir, "rebase-merge");
+
+void rebase_merge_init(struct rebase_merge *state, const char *dir)
+{
+   if (!dir)
+   dir = git_path_rebase_merge_dir();
+   rebase_options_init(>opts);
+   state->dir = xstrdup(dir);
+   state->msgnum = 0;
+   state->end = 0;
+   state->prec = 4;
+}
+
+void rebase_merge_release(struct rebase_merge *state)
+{
+   rebase_options_release(>opts);
+   free(state->dir);
+}
+
+int rebase_merge_in_progress(const struct rebase_merge *state)
+{
+   const char *dir = state ? state->dir : git_path_rebase_merge_dir();
+   struct stat st;
+
+   if (lstat(dir, ) || !S_ISDIR(st.st_mode))
+   return 0;
+
+   if (file_exists(mkpath("%s/interactive", dir)))
+   return 0;
+
+   return 1;
+}
+
+static const char *state_path(const struct rebase_merge *state, const char 
*filename)
+{
+   return mkpath("%s/%s", state->dir, filename);
+}
+
+static int read_state_file(const struct rebase_merge *state, const char 
*filename, struct strbuf *sb)
+{
+   const char *path = state_path(state, filename);
+   if (strbuf_read_file(sb, path, 0) < 0)
+   return error(_("could not read file %s"), path);
+ 

[PATCH/RFC/GSoC 07/17] rebase-common: implement refresh_and_write_cache()

2016-03-12 Thread Paul Tan
In the upcoming git-rebase to C rewrite, it is a common operation to
refresh the index and write the resulting index.

builtin/am.c already implements refresh_and_write_cache(), which is what
we want. Move it to rebase-common.c, so that it can be shared with all
the rebase backends, including git-am.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 builtin/am.c| 14 +-
 rebase-common.c | 11 +++
 rebase-common.h |  5 +
 3 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index d003939..504b604 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -28,6 +28,7 @@
 #include "rerere.h"
 #include "prompt.h"
 #include "mailinfo.h"
+#include "rebase-common.h"
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -1125,19 +1126,6 @@ static const char *msgnum(const struct am_state *state)
 }
 
 /**
- * Refresh and write index.
- */
-static void refresh_and_write_cache(void)
-{
-   struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-
-   hold_locked_index(lock_file, 1);
-   refresh_cache(REFRESH_QUIET);
-   if (write_locked_index(_index, lock_file, COMMIT_LOCK))
-   die(_("unable to write index file"));
-}
-
-/**
  * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
  * branch, returns 1 if there are entries in the index, 0 otherwise. If an
  * strbuf is provided, the space-separated list of files that differ will be
diff --git a/rebase-common.c b/rebase-common.c
index 8169fb6..b07e1f1 100644
--- a/rebase-common.c
+++ b/rebase-common.c
@@ -3,6 +3,17 @@
 #include "dir.h"
 #include "run-command.h"
 #include "refs.h"
+#include "lockfile.h"
+
+void refresh_and_write_cache(void)
+{
+   struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+   hold_locked_index(lock_file, 1);
+   refresh_cache(REFRESH_QUIET);
+   if (write_locked_index(_index, lock_file, COMMIT_LOCK))
+   die(_("unable to write index file"));
+}
 
 void rebase_options_init(struct rebase_options *opts)
 {
diff --git a/rebase-common.h b/rebase-common.h
index 067ad0b..8620e8c 100644
--- a/rebase-common.h
+++ b/rebase-common.h
@@ -1,6 +1,11 @@
 #ifndef REBASE_COMMON_H
 #define REBASE_COMMON_H
 
+/**
+ * Refresh and write index.
+ */
+void refresh_and_write_cache(void);
+
 /* common rebase backend options */
 struct rebase_options {
struct object_id onto;
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 17/17] rebase-interactive: introduce interactive backend for builtin rebase

2016-03-12 Thread Paul Tan
Since 1b1dce4 (Teach rebase an interactive mode, 2007-06-25), git-rebase
supports an interactive mode when passed the -i switch.

In interactive mode, git-rebase allows users to edit the list of patches
(using the user's GIT_SEQUENCE_EDITOR), so that the user can reorder,
edit and delete patches.

Re-implement a skeletal version of the above feature by introducing a
rebase-interactive backend for our builtin-rebase. This skeletal
implementation is only able to pick and re-order commits.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 Makefile |   1 +
 builtin/rebase.c |  17 ++-
 rebase-interactive.c | 375 +++
 rebase-interactive.h |  33 +
 4 files changed, 424 insertions(+), 2 deletions(-)
 create mode 100644 rebase-interactive.c
 create mode 100644 rebase-interactive.h

diff --git a/Makefile b/Makefile
index 8b928e4..3bd3127 100644
--- a/Makefile
+++ b/Makefile
@@ -781,6 +781,7 @@ LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += rebase-am.o
 LIB_OBJS += rebase-common.o
+LIB_OBJS += rebase-interactive.o
 LIB_OBJS += rebase-merge.o
 LIB_OBJS += rebase-todo.o
 LIB_OBJS += reflog-walk.o
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6d42115..d811a44 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -10,11 +10,13 @@
 #include "refs.h"
 #include "rebase-am.h"
 #include "rebase-merge.h"
+#include "rebase-interactive.h"
 
 enum rebase_type {
REBASE_TYPE_NONE = 0,
REBASE_TYPE_AM,
-   REBASE_TYPE_MERGE
+   REBASE_TYPE_MERGE,
+   REBASE_TYPE_INTERACTIVE
 };
 
 static const char *rebase_dir(enum rebase_type type)
@@ -24,6 +26,8 @@ static const char *rebase_dir(enum rebase_type type)
return git_path_rebase_am_dir();
case REBASE_TYPE_MERGE:
return git_path_rebase_merge_dir();
+   case REBASE_TYPE_INTERACTIVE:
+   return git_path_rebase_interactive_dir();
default:
die("BUG: invalid rebase_type %d", type);
}
@@ -142,6 +146,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
const char *onto_name = NULL;
const char *branch_name;
int do_merge = 0;
+   int interactive = 0;
 
const char * const usage[] = {
N_("git rebase [options] [--onto ] [] 
[]"),
@@ -153,6 +158,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("rebase onto given branch instead of upstream")),
OPT_BOOL('m', "merge", _merge,
N_("use merging strategies to rebase")),
+   OPT_BOOL('i', "interactive", ,
+   N_("let the user edit the list of commits to rebase")),
OPT_END()
};
 
@@ -232,7 +239,13 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
 
/* Run the appropriate rebase backend */
-   if (do_merge) {
+   if (interactive) {
+   struct rebase_interactive state;
+   rebase_interactive_init(, 
rebase_dir(REBASE_TYPE_INTERACTIVE));
+   rebase_options_swap(, _opts);
+   rebase_interactive_run();
+   rebase_interactive_release();
+   } else if (do_merge) {
struct rebase_merge state;
rebase_merge_init(, rebase_dir(REBASE_TYPE_MERGE));
rebase_options_swap(, _opts);
diff --git a/rebase-interactive.c b/rebase-interactive.c
new file mode 100644
index 000..342a6fe
--- /dev/null
+++ b/rebase-interactive.c
@@ -0,0 +1,375 @@
+#include "cache.h"
+#include "rebase-interactive.h"
+#include "argv-array.h"
+#include "revision.h"
+#include "dir.h"
+#include "run-command.h"
+
+static int is_empty_commit(struct commit *commit)
+{
+   if (commit->parents)
+   return !oidcmp(>object.oid, 
>parents->item->object.oid);
+   else
+   return !hashcmp(commit->object.oid.hash, EMPTY_TREE_SHA1_BIN);
+}
+
+GIT_PATH_FUNC(git_path_rebase_interactive_dir, "rebase-merge")
+
+void rebase_interactive_init(struct rebase_interactive *state, const char *dir)
+{
+   rebase_options_init(>opts);
+   if (!dir)
+   dir = git_path_rebase_interactive_dir();
+   state->dir = xstrdup(dir);
+
+   state->todo_file = mkpathdup("%s/git-rebase-todo", state->dir);
+   rebase_todo_list_init(>todo);
+   state->todo_offset = 0;
+   state->todo_count = 0;
+
+   state->done_file = mkpathdup("%s/done", state->dir);
+   state->done_count = 0;
+
+   state->instruction_format = NULL;
+   git_config_get_value("rebase.instructionFormat", 
>instruction_format);
+}
+
+void rebase_interactive_

[PATCH/RFC/GSoC 15/17] wrapper: implement append_file()

2016-03-12 Thread Paul Tan
Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 cache.h   |  1 +
 wrapper.c | 23 +++
 2 files changed, 24 insertions(+)

diff --git a/cache.h b/cache.h
index 55d443e..aa5e97c 100644
--- a/cache.h
+++ b/cache.h
@@ -1700,6 +1700,7 @@ static inline ssize_t write_str_in_full(int fd, const 
char *str)
 
 extern int write_file(const char *path, const char *fmt, ...);
 extern int write_file_gently(const char *path, const char *fmt, ...);
+extern void append_file(const char *path, const char *fmt, ...);
 
 /* pager.c */
 extern void setup_pager(void);
diff --git a/wrapper.c b/wrapper.c
index 9afc1a0..cd77e94 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -709,6 +709,29 @@ int write_file_gently(const char *path, const char *fmt, 
...)
return status;
 }
 
+void append_file(const char *path, const char *fmt, ...)
+{
+   struct strbuf sb = STRBUF_INIT;
+   int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0666);
+   va_list params;
+   if (fd < 0)
+   die_errno(_("could not open %s for appending"), path);
+   va_start(params, fmt);
+   strbuf_vaddf(, fmt, params);
+   va_end(params);
+   strbuf_complete_line();
+   if (write_in_full(fd, sb.buf, sb.len) != sb.len) {
+   int err = errno;
+   close(fd);
+   strbuf_release();
+   errno = err;
+   die_errno(_("could not write to %s"), path);
+   }
+   strbuf_release();
+   if (close(fd))
+   die_errno(_("could not close %s"), path);
+}
+
 void sleep_millisec(int millisec)
 {
poll(NULL, 0, millisec);
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 05/17] rebase-options: implement rebase_options_load() and rebase_options_save()

2016-03-12 Thread Paul Tan
These functions can be used for loading and saving common rebase options
into a state directory.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 rebase-common.c | 69 +
 rebase-common.h |  4 
 2 files changed, 73 insertions(+)

diff --git a/rebase-common.c b/rebase-common.c
index 5a49ac4..1835f08 100644
--- a/rebase-common.c
+++ b/rebase-common.c
@@ -26,3 +26,72 @@ void rebase_options_swap(struct rebase_options *dst, struct 
rebase_options *src)
*dst = *src;
*src = tmp;
 }
+
+static int state_file_exists(const char *dir, const char *file)
+{
+   return file_exists(mkpath("%s/%s", dir, file));
+}
+
+static int read_state_file(struct strbuf *sb, const char *dir, const char 
*file)
+{
+   const char *path = mkpath("%s/%s", dir, file);
+   strbuf_reset(sb);
+   if (strbuf_read_file(sb, path, 0) >= 0)
+   return sb->len;
+   else
+   return error(_("could not read '%s'"), path);
+}
+
+int rebase_options_load(struct rebase_options *opts, const char *dir)
+{
+   struct strbuf sb = STRBUF_INIT;
+   const char *filename;
+
+   /* opts->orig_refname */
+   if (read_state_file(, dir, "head-name") < 0)
+   return -1;
+   strbuf_trim();
+   if (starts_with(sb.buf, "refs/heads/"))
+   opts->orig_refname = strbuf_detach(, NULL);
+   else if (!strcmp(sb.buf, "detached HEAD"))
+   opts->orig_refname = NULL;
+   else
+   return error(_("could not parse %s"), mkpath("%s/%s", dir, 
"head-name"));
+
+   /* opts->onto */
+   if (read_state_file(, dir, "onto") < 0)
+   return -1;
+   strbuf_trim();
+   if (get_oid_hex(sb.buf, >onto) < 0)
+   return error(_("could not parse %s"), mkpath("%s/%s", dir, 
"onto"));
+
+   /*
+* We always write to orig-head, but interactive rebase used to write
+* to head. Fall back to reading from head to cover for the case that
+* the user upgraded git with an ongoing interactive rebase.
+*/
+   filename = state_file_exists(dir, "orig-head") ? "orig-head" : "head";
+   if (read_state_file(, dir, filename) < 0)
+   return -1;
+   strbuf_trim();
+   if (get_oid_hex(sb.buf, >orig_head) < 0)
+   return error(_("could not parse %s"), mkpath("%s/%s", dir, 
filename));
+
+   strbuf_release();
+   return 0;
+}
+
+static int write_state_text(const char *dir, const char *file, const char 
*string)
+{
+   return write_file(mkpath("%s/%s", dir, file), "%s", string);
+}
+
+void rebase_options_save(const struct rebase_options *opts, const char *dir)
+{
+   const char *head_name = opts->orig_refname;
+   if (!head_name)
+   head_name = "detached HEAD";
+   write_state_text(dir, "head-name", head_name);
+   write_state_text(dir, "onto", oid_to_hex(>onto));
+   write_state_text(dir, "orig-head", oid_to_hex(>orig_head));
+}
diff --git a/rebase-common.h b/rebase-common.h
index db5146a..051c056 100644
--- a/rebase-common.h
+++ b/rebase-common.h
@@ -20,4 +20,8 @@ void rebase_options_release(struct rebase_options *);
 
 void rebase_options_swap(struct rebase_options *dst, struct rebase_options 
*src);
 
+int rebase_options_load(struct rebase_options *, const char *dir);
+
+void rebase_options_save(const struct rebase_options *, const char *dir);
+
 #endif /* REBASE_COMMON_H */
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 03/17] builtin-rebase: implement skeletal builtin rebase

2016-03-12 Thread Paul Tan
Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 Makefile |  5 +
 builtin.h|  1 +
 builtin/rebase.c | 31 +++
 git.c|  1 +
 4 files changed, 34 insertions(+), 4 deletions(-)
 create mode 100644 builtin/rebase.c

diff --git a/Makefile b/Makefile
index 24bef8d..ad98714 100644
--- a/Makefile
+++ b/Makefile
@@ -496,7 +496,6 @@ SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -505,9 +504,6 @@ SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
-SCRIPT_LIB += git-rebase--am
-SCRIPT_LIB += git-rebase--interactive
-SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
 
@@ -909,6 +905,7 @@ BUILTIN_OBJS += builtin/prune.o
 BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/rebase.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
 BUILTIN_OBJS += builtin/remote.o
diff --git a/builtin.h b/builtin.h
index 6b95006..a184a58 100644
--- a/builtin.h
+++ b/builtin.h
@@ -102,6 +102,7 @@ extern int cmd_prune_packed(int argc, const char **argv, 
const char *prefix);
 extern int cmd_pull(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 000..04cc1bd
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,31 @@
+/*
+ * Builtin "git rebase"
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static int git_rebase_config(const char *k, const char *v, void *cb)
+{
+   return git_default_config(k, v, NULL);
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+   const char * const usage[] = {
+   N_("git rebase [options]"),
+   NULL
+   };
+   struct option options[] = {
+   OPT_END()
+   };
+
+   git_config(git_rebase_config, NULL);
+
+   argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+   if (read_cache_preload(NULL) < 0)
+   die(_("failed to read the index"));
+
+   return 0;
+}
diff --git a/git.c b/git.c
index 6cc0c07..f9b7033 100644
--- a/git.c
+++ b/git.c
@@ -452,6 +452,7 @@ static struct cmd_struct commands[] = {
{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
{ "push", cmd_push, RUN_SETUP },
{ "read-tree", cmd_read_tree, RUN_SETUP },
+   { "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 02/17] sha1_name: implement get_oid() and friends

2016-03-12 Thread Paul Tan
5f7817c (define a structure for object IDs, 2015-03-13) introduced the
object_id struct to replace the used of unsigned char[] arrays to hold
object IDs. This gives us the benefit of compile-time checking for
misuse.

To fully take advantage of compile-time type-checking, introduce the
get_oid_*() functions which wrap the corresponding get_sha1_*()
functions.

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 cache.h |  6 ++
 sha1_name.c | 30 ++
 2 files changed, 36 insertions(+)

diff --git a/cache.h b/cache.h
index b829410..55d443e 100644
--- a/cache.h
+++ b/cache.h
@@ -1116,11 +1116,17 @@ struct object_context {
 #define GET_SHA1_ONLY_TO_DIE04000
 
 extern int get_sha1(const char *str, unsigned char *sha1);
+extern int get_oid(const char *str, struct object_id *oid);
 extern int get_sha1_commit(const char *str, unsigned char *sha1);
+extern int get_oid_commit(const char *str, struct object_id *oid);
 extern int get_sha1_committish(const char *str, unsigned char *sha1);
+extern int get_oid_committish(const char *str, struct object_id *oid);
 extern int get_sha1_tree(const char *str, unsigned char *sha1);
+extern int get_oid_tree(const char *str, struct object_id *oid);
 extern int get_sha1_treeish(const char *str, unsigned char *sha1);
+extern int get_oid_treeish(const char *str, struct object_id *oid);
 extern int get_sha1_blob(const char *str, unsigned char *sha1);
+extern int get_oid_blob(const char *str, struct object_id *oid);
 extern void maybe_die_on_misspelt_object_name(const char *name, const char 
*prefix);
 extern int get_sha1_with_context(const char *str, unsigned flags, unsigned 
char *sha1, struct object_context *orc);
 
diff --git a/sha1_name.c b/sha1_name.c
index 3acf221..307dfad 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1214,6 +1214,11 @@ int get_sha1(const char *name, unsigned char *sha1)
return get_sha1_with_context(name, 0, sha1, );
 }
 
+int get_oid(const char *name, struct object_id *oid)
+{
+   return get_sha1(name, oid->hash);
+}
+
 /*
  * Many callers know that the user meant to name a commit-ish by
  * syntactical positions where the object name appears.  Calling this
@@ -1231,6 +1236,11 @@ int get_sha1_committish(const char *name, unsigned char 
*sha1)
 sha1, );
 }
 
+int get_oid_committish(const char *name, struct object_id *oid)
+{
+   return get_sha1_committish(name, oid->hash);
+}
+
 int get_sha1_treeish(const char *name, unsigned char *sha1)
 {
struct object_context unused;
@@ -1238,6 +1248,11 @@ int get_sha1_treeish(const char *name, unsigned char 
*sha1)
 sha1, );
 }
 
+int get_oid_treeish(const char *name, struct object_id *oid)
+{
+   return get_sha1_treeish(name, oid->hash);
+}
+
 int get_sha1_commit(const char *name, unsigned char *sha1)
 {
struct object_context unused;
@@ -1245,6 +1260,11 @@ int get_sha1_commit(const char *name, unsigned char 
*sha1)
 sha1, );
 }
 
+int get_oid_commit(const char *name, struct object_id *oid)
+{
+   return get_sha1_commit(name, oid->hash);
+}
+
 int get_sha1_tree(const char *name, unsigned char *sha1)
 {
struct object_context unused;
@@ -1252,6 +1272,11 @@ int get_sha1_tree(const char *name, unsigned char *sha1)
 sha1, );
 }
 
+int get_oid_tree(const char *name, struct object_id *oid)
+{
+   return get_sha1_tree(name, oid->hash);
+}
+
 int get_sha1_blob(const char *name, unsigned char *sha1)
 {
struct object_context unused;
@@ -1259,6 +1284,11 @@ int get_sha1_blob(const char *name, unsigned char *sha1)
 sha1, );
 }
 
+int get_oid_blob(const char *name, struct object_id *oid)
+{
+   return get_sha1_blob(name, oid->hash);
+}
+
 /* Must be called only when object_name:filename doesn't exist. */
 static void diagnose_invalid_sha1_path(const char *prefix,
   const char *filename,
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 00/17] A barebones git-rebase in C

2016-03-12 Thread Paul Tan
 code from builtin/am.c as
well.

Regards,
Paul

Paul Tan (17):
  perf: introduce performance tests for git-rebase
  sha1_name: implement get_oid() and friends
  builtin-rebase: implement skeletal builtin rebase
  builtin-rebase: parse rebase arguments into a common rebase_options
struct
  rebase-options: implement rebase_options_load() and
rebase_options_save()
  rebase-am: introduce am backend for builtin rebase
  rebase-common: implement refresh_and_write_cache()
  rebase-common: let refresh_and_write_cache() take a flags argument
  rebase-common: implement cache_has_unstaged_changes()
  rebase-common: implement cache_has_uncommitted_changes()
  rebase-merge: introduce merge backend for builtin rebase
  rebase-todo: introduce rebase_todo_item
  rebase-todo: introduce rebase_todo_list
  status: use rebase_todo_list
  wrapper: implement append_file()
  editor: implement git_sequence_editor() and launch_sequence_editor()
  rebase-interactive: introduce interactive backend for builtin rebase

 Makefile   |  10 +-
 builtin.h  |   1 +
 builtin/am.c   |  16 +-
 builtin/pull.c |  41 +---
 builtin/rebase.c   | 264 ++
 cache.h|   8 +
 editor.c   |  27 ++-
 git.c  |   1 +
 rebase-am.c| 110 +++
 rebase-am.h|  22 +++
 rebase-common.c| 220 ++
 rebase-common.h|  48 +
 rebase-interactive.c   | 375 +
 rebase-interactive.h   |  33 
 rebase-merge.c | 256 +
 rebase-merge.h |  28 +++
 rebase-todo.c  | 251 +
 rebase-todo.h  |  55 ++
 sha1_name.c|  30 +++
 strbuf.h   |   1 +
 t/perf/p3400-rebase.sh |  25 +++
 t/perf/p3402-rebase-merge.sh   |  25 +++
 t/perf/p3404-rebase-interactive.sh |  26 +++
 wrapper.c  |  23 +++
 wt-status.c| 100 +++---
 25 files changed, 1863 insertions(+), 133 deletions(-)
 create mode 100644 builtin/rebase.c
 create mode 100644 rebase-am.c
 create mode 100644 rebase-am.h
 create mode 100644 rebase-common.c
 create mode 100644 rebase-common.h
 create mode 100644 rebase-interactive.c
 create mode 100644 rebase-interactive.h
 create mode 100644 rebase-merge.c
 create mode 100644 rebase-merge.h
 create mode 100644 rebase-todo.c
 create mode 100644 rebase-todo.h
 create mode 100755 t/perf/p3400-rebase.sh
 create mode 100755 t/perf/p3402-rebase-merge.sh
 create mode 100755 t/perf/p3404-rebase-interactive.sh

-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH/RFC/GSoC 01/17] perf: introduce performance tests for git-rebase

2016-03-12 Thread Paul Tan
To determine the speedup (or slowdown) of the upcoming git-rebase
rewrite to C, add a simple performance test for each of the 3 git-rebase
backends (am, merge and interactive).

Signed-off-by: Paul Tan <pyoka...@gmail.com>
---
 t/perf/p3400-rebase.sh | 25 +
 t/perf/p3402-rebase-merge.sh   | 25 +
 t/perf/p3404-rebase-interactive.sh | 26 ++
 3 files changed, 76 insertions(+)
 create mode 100755 t/perf/p3400-rebase.sh
 create mode 100755 t/perf/p3402-rebase-merge.sh
 create mode 100755 t/perf/p3404-rebase-interactive.sh

diff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh
new file mode 100755
index 000..f172a64
--- /dev/null
+++ b/t/perf/p3400-rebase.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description="Tests rebase performance with am backend"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+# Setup a topic branch with 50 commits
+test_expect_success 'setup topic branch' '
+   git checkout -b perf-topic-branch master &&
+   for i in $(test_seq 50); do
+   test_commit perf-$i file
+   done &&
+   git tag perf-topic-branch-initial
+'
+
+test_perf 'rebase --onto master^' '
+   git checkout perf-topic-branch &&
+   git reset --hard perf-topic-branch-initial &&
+   git rebase --onto master^ master
+'
+
+test_done
diff --git a/t/perf/p3402-rebase-merge.sh b/t/perf/p3402-rebase-merge.sh
new file mode 100755
index 000..b71ce12
--- /dev/null
+++ b/t/perf/p3402-rebase-merge.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description="Tests rebase performance with merge backend"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+# Setup a topic branch with 50 commits
+test_expect_success 'setup topic branch' '
+   git checkout -b perf-topic-branch master &&
+   for i in $(test_seq 50); do
+   test_commit perf-$i file
+   done &&
+   git tag perf-topic-branch-initial
+'
+
+test_perf 'rebase -m --onto master^' '
+   git checkout perf-topic-branch &&
+   git reset --hard perf-topic-branch-initial &&
+   git rebase -m --onto master^ master
+'
+
+test_done
diff --git a/t/perf/p3404-rebase-interactive.sh 
b/t/perf/p3404-rebase-interactive.sh
new file mode 100755
index 000..aaca105
--- /dev/null
+++ b/t/perf/p3404-rebase-interactive.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description="Tests interactive rebase performance"
+
+. ./perf-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+# Setup a topic branch with 50 commits
+test_expect_success 'setup topic branch' '
+   git checkout -b perf-topic-branch master &&
+   for i in $(test_seq 50); do
+   test_commit perf-$i file
+   done &&
+   git tag perf-topic-branch-initial
+'
+
+test_perf 'rebase -i --onto master^' '
+   git checkout perf-topic-branch &&
+   git reset --hard perf-topic-branch-initial &&
+   GIT_SEQUENCE_EDITOR=: git rebase -i --onto master^ master
+'
+
+test_done
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v7 2/2] pull --rebase: add --[no-]autostash flag

2016-03-10 Thread Paul Tan
On Wed, Mar 9, 2016 at 12:18 PM, Mehul Jain <mehul.jain2...@gmail.com> wrote:
> If rebase.autoStash configuration variable is set, there is no way to
> override it for "git pull --rebase" from the command line.
>
> Teach "git pull --rebase" the --[no-]autostash command line flag which
> overrides the current value of rebase.autoStash, if set. As "git rebase"
> understands the --[no-]autostash option, it's just a matter of passing
> the option to underlying "git rebase" when "git pull --rebase" is called.
>
> Helped-by: Matthieu Moy <matthieu....@grenoble-inp.fr>
> Helped-by: Junio C Hamano <gits...@pobox.com>
> Helped-by: Paul Tan <pyoka...@gmail.com>
> Helped-by: Eric Sunshine <sunsh...@sunshineco.com>
> Signed-off-by: Mehul Jain <mehul.jain2...@gmail.com>
> ---
> Previous patches: $gname287709
>
> Changes:
> - Slight change is documentation.
>
>  Documentation/git-pull.txt |  9 +
>  builtin/pull.c | 16 ++--
>  t/t5520-pull.sh| 39 +++
>  3 files changed, 62 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
> index a62a2a6..da89be6 100644
> --- a/Documentation/git-pull.txt
> +++ b/Documentation/git-pull.txt
> @@ -128,6 +128,15 @@ unless you have read linkgit:git-rebase[1] carefully.
>  --no-rebase::
> Override earlier --rebase.
>
> +--autostash::
> +--no-autostash::
> +   Before starting rebase, stash local modifications away (see
> +   linkgit:git-stash.txt[1]) if needed, and apply the stash when
> +   done (this option is only valid when "--rebase" is used).
> ++
> +`--no-autostash` is useful to override the `rebase.autoStash`
> +configuration variable (see linkgit:git-config[1]).
> +
>  Options related to fetching
>  ~~~
>
> diff --git a/builtin/pull.c b/builtin/pull.c
> index 8a318e9..a01058a 100644
> --- a/builtin/pull.c
> +++ b/builtin/pull.c
> @@ -86,6 +86,7 @@ static char *opt_commit;
>  static char *opt_edit;
>  static char *opt_ff;
>  static char *opt_verify_signatures;
> +static int opt_autostash = -1;
>  static int config_autostash = -1;

Hmm, why can't config_autostash just default to 0?

>  static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
>  static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
> @@ -150,6 +151,8 @@ static struct option pull_options[] = {
> OPT_PASSTHRU(0, "verify-signatures", _verify_signatures, NULL,
> N_("verify that the named commit has a valid GPG signature"),
> PARSE_OPT_NOARG),
> +   OPT_BOOL(0, "autostash", _autostash,
> +   N_("automatically stash/stash pop before and after rebase")),
> OPT_PASSTHRU_ARGV('s', "strategy", _strategies, N_("strategy"),
> N_("merge strategy to use"),
> 0),
> @@ -801,6 +804,10 @@ static int run_rebase(const unsigned char *curr_head,
> argv_array_pushv(, opt_strategy_opts.argv);
> if (opt_gpg_sign)
> argv_array_push(, opt_gpg_sign);
> +   if (opt_autostash == 1)
> +   argv_array_push(, "--autostash");
> +   else if (opt_autostash == 0)
> +   argv_array_push(, "--no-autostash");

The precise testing for specific values of -1, 0 and 1 throughout the
code makes me uncomfortable. Ordinarily, I would expect a simple

argv_array_push(, opt_autostash ? "--autostash" : "--no-autostash");

Stepping back a bit, the only reason why we introduced opt_autostash =
-1 as a possible value is because we need to detect if
--[no-]autostash is being used with git-merge. I consider that a
kludge, because if git-merge supports --autostash as well (possibly in
the future), then we will not need this -1 value.

So, from that point of view, a -1 value is okay as a workaround, but
kludges, and hence the -1 value, should be gotten rid off as soon as
possible.

>
> argv_array_push(, "--onto");
> argv_array_push(, sha1_to_hex(merge_head));
> @@ -851,12 +858,17 @@ int cmd_pull(int argc, const char **argv, const char 
> *prefix)
> if (is_null_sha1(orig_head) && !is_cache_unborn())
> die(_("Updating an unborn branch with changes added 
> to the index."));
>
> -   if (config_autostash != 1)
> +   if (opt_autostash == -1)
> +   opt_autostash = config_autostash;

So, if config_autostash defaults to zero, we can be certain that

Re: [PATCH v3 3/3] Documentation/git-pull: document --[no-]autostash option

2016-03-04 Thread Paul Tan
On Fri, Mar 4, 2016 at 12:13 AM, Mehul Jain  wrote:
> Signed-off-by: Mehul Jain 
> ---
>  Documentation/git-pull.txt | 15 +++
>  1 file changed, 15 insertions(+)
>
> diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
> index a62a2a6..a593972 100644
> --- a/Documentation/git-pull.txt
> +++ b/Documentation/git-pull.txt
> @@ -128,6 +128,21 @@ unless you have read linkgit:git-rebase[1] carefully.
>  --no-rebase::
> Override earlier --rebase.
>
> +--autostash::
> +--no-autostash::
> +   Automatically create a temporary stash before the operation
> +   begins, and apply it after the operation ends. This means
> +   that you can run rebase on a dirty worktree.
> ++
> +This option is only valid when '--rebase' option is used.
> ++
> +The default is --no-autostash, unless rebase.autoStash configuration

By the way, there is a trailing space on this line.

> +is set.
> ++
> +[NOTE]
> +Use with care: the final stash application after a successful
> +rebase might result in non-trivial conflicts.
> +
>  Options related to fetching
>  ~~~
>
> --
> 2.7.1.340.g69eb491.dirty

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 1/3] pull --rebase: add --[no-]autostash flag

2016-03-04 Thread Paul Tan
On Fri, Mar 4, 2016 at 12:13 AM, Mehul Jain  wrote:
> diff --git a/builtin/pull.c b/builtin/pull.c
> index 10eff03..b338b83 100644
> --- a/builtin/pull.c
> +++ b/builtin/pull.c
> @@ -85,6 +85,7 @@ static char *opt_squash;
>  static char *opt_commit;
>  static char *opt_edit;
>  static char *opt_ff;
> +static int opt_autostash = -1;
>  static char *opt_verify_signatures;
>  static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
>  static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
> @@ -146,6 +147,8 @@ static struct option pull_options[] = {
> OPT_PASSTHRU(0, "ff-only", _ff, NULL,
> N_("abort if fast-forward is not possible"),
> PARSE_OPT_NOARG | PARSE_OPT_NONEG),
> +   OPT_BOOL(0, "autostash", _autostash,
> +   N_("automatically stash/stash pop before and after rebase")),
> OPT_PASSTHRU(0, "verify-signatures", _verify_signatures, NULL,
> N_("verify that the named commit has a valid GPG signature"),
> PARSE_OPT_NOARG),
> @@ -789,7 +792,8 @@ static int run_rebase(const unsigned char *curr_head,
> argv_array_pushv(, opt_strategy_opts.argv);
> if (opt_gpg_sign)
> argv_array_push(, opt_gpg_sign);
> -

Minor nit: but when I wrote the code for run_rebase() I separated the
options for "Shared options" and "Options passed to git-rebase" into
different code block groups from the other code, and I would like it
if it remained that way :-(

> +   if (opt_autostash)
> +   argv_array_push(, "--autostash");

Hmm, interesting. If rebase.autostash=true !opt_autostash, we don't
need to pass --no-autostash to git-rebase because it will only stash
if the worktree is dirty, but a dirty worktree will be caught by
git-pull's die_on_unclean_worktree() anyway.

Still, it may be a problem because the worktree may become dirty
in-between our "worktree is clean" check and when git-rebase is run
(during the git-fetch), and the user may be surprised if git-rebase
attempted to stash in that case.

So we may wish to pass "--no-autostash" to git-rebase as well.

> argv_array_push(, "--onto");
> argv_array_push(, sha1_to_hex(merge_head));
>
> @@ -835,18 +839,25 @@ int cmd_pull(int argc, const char **argv, const char 
> *prefix)
> hashclr(orig_head);
>
> if (opt_rebase) {
> -   int autostash = 0;
> -
> if (is_null_sha1(orig_head) && !is_cache_unborn())
> die(_("Updating an unborn branch with changes added 
> to the index."));
>
> -   git_config_get_bool("rebase.autostash", );
> -   if (!autostash)
> +   if (opt_autostash == -1)
> +   git_config_get_bool("rebase.autostash", 
> _autostash);
> +
> +   if (opt_autostash == 0 || opt_autostash == -1)
> die_on_unclean_work_tree(prefix);
>
> if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
> hashclr(rebase_fork_point);
> }
> +   else {

Git code style puts the else on the same line, not on a new one.

> +   /* If --[no-]autostash option is called without --rebase */

Yeah, I agree with Eric that this comment should be dropped,

> +   if (opt_autostash == 0)
> +   die(_("--no-autostash option is only valid with 
> --rebase."));
> +   else if (opt_autostash == 1)
> +   die(_("--autostash option is only valid with 
> --rebase."));
> +   }

and these error messages combined.

>
> if (run_fetch(repo, refspecs))
> return 1;
> --
> 2.7.1.340.g69eb491.dirty

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2 1/2] pull --rebase: add --[no-]autostash flag

2016-02-28 Thread Paul Tan
Hi Junio,

On Sun, Feb 28, 2016 at 3:26 AM, Junio C Hamano  wrote:
> Mehul Jain  writes:
>> @@ -835,13 +841,10 @@ int cmd_pull(int argc, const char **argv, const char 
>> *prefix)
>>   hashclr(orig_head);
>>
>>   if (opt_rebase) {
>> - int autostash = 0;
>> -
>>   if (is_null_sha1(orig_head) && !is_cache_unborn())
>>   die(_("Updating an unborn branch with changes added to 
>> the index."));
>>
>> - git_config_get_bool("rebase.autostash", );
>> - if (!autostash)
>> + if (!opt_autostash)
>>   die_on_unclean_work_tree(prefix);
>
> I would have expected that
>
>  * a global opt_autostash is initialized to -1 (unspecified);
>
>  * opt_bool() would flip it to either 0 or 1 with --[no-]autostash;
>
>  * existing "rebase.autostash" configuration check inside "git pull"
>code  gets removed;

Removing the "rebase.autostash" configuration check would bring back
the problem which 53c76dc (pull: allow dirty tree when
rebase.autostash enabled, 2015-07-04) fixed.

>  * and the code that builds "git rebase" invocation command line
>will do
>
> if (opt_autostash < 0)
> ; /* do nothing */
> else if (opt_autostash == 0)
> argv_array_push(, "--no-autostash");
> else
> argv_array_push(, "--autostash");
>
> Then when "git pull --rebase" is run without "--[no-]autostash", the
> underlying "git rebase" would be run without that option, and does its
> usual thing, including reading rebase.autostash and deciding to do
> "git stash".  And when "git pull" is run with "--[no-]autostash",
> the underlying "git rebase" would be given the same option, and
> would do what it was told to do, ignoring rebase.autostash setting.
>
> So why does "git pull" still need to look at rebase.autostash
> configuration after this change?

Ultimately, git-pull needs to be aware of whether autostash is active
or not (and this means rebase.autostash needs to be looked at as well)
because if autostash is disabled, git-pull needs to perform the
"worktree is clean" check. And this "worktree is clean" check needs to
be done *before* git-fetch and git-rebase is run.

See f9189cf (pull --rebase: exit early when the working directory is
dirty, 2008-05-21).

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] Add --no-autostash flag to git pull --rebase

2016-02-26 Thread Paul Tan
On Fri, Feb 26, 2016 at 7:23 PM, Mehul Jain  wrote:
> Subject: [PATCH] Add --no-autostash flag to git pull --rebase

We usually don't capitalize the first word of the commit title. We
also usually prefix the commit title with the relevant subsystem, file
or command. So something like:

pull --rebase: add --[no-]autostash flag

Some grammatical/spelling nits below:

> git pull --rebase now understand --no-autostash flag.

s/understand/understands the/

> If directory is found to be dirty then command will die.

If the worktree is found to be dirty then the command will die.

(Perhaps state more clearly that the dirty worktree check is only
performed on --no-autostash or rebase.autostash=false.)

> This flag override "rebase.autostash" configuration(if set).

s/override/overrides the/

> If this flag is not passed in command line then default behaviour is choosen,

s/choosen/chosen/

> given by "rebase.autostash"(if "rebase.autostash"
> is not set then git pull --rebase will die if directory is dirty).

If "rebase.autostash" is not set or is false. Or you could shorten it
by saying that "(default is false)".

> Signed-off-by: Mehul Jain 
> ---
>  builtin/pull.c  | 12 
>  t/t5520-pull.sh |  8 
>  t/t5521-pull-options.sh | 24 
>  3 files changed, 40 insertions(+), 4 deletions(-)

I think git-pull's documentation should be updated as well to talk
about this new command-line switch.

> diff --git a/builtin/pull.c b/builtin/pull.c
> index 10eff03..9d1a3d0 100644
> --- a/builtin/pull.c
> +++ b/builtin/pull.c
> @@ -85,6 +85,7 @@ static char *opt_squash;
>  static char *opt_commit;
>  static char *opt_edit;
>  static char *opt_ff;
> +static int opt_autostash = -1;
>  static char *opt_verify_signatures;
>  static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
>  static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
> @@ -146,6 +147,8 @@ static struct option pull_options[] = {
> OPT_PASSTHRU(0, "ff-only", _ff, NULL,
> N_("abort if fast-forward is not possible"),
> PARSE_OPT_NOARG | PARSE_OPT_NONEG),
> +   OPT_COLOR_FLAG(0,"autostash",_autostash,
> +   N_("abort if tree is dirty")),

Why OPT_COLOR_FLAG()? And --autostash is not just about aborting if
the working tree is dirty. Why not just copy the help message from
git-rebase? Something like:

"automatically stash/stash pop before and after rebase"

> OPT_PASSTHRU(0, "verify-signatures", _verify_signatures, NULL,
> N_("verify that the named commit has a valid GPG signature"),
> PARSE_OPT_NOARG),
> @@ -835,13 +838,14 @@ int cmd_pull(int argc, const char **argv, const char 
> *prefix)
> hashclr(orig_head);
>
> if (opt_rebase) {
> -   int autostash = 0;
> -
> if (is_null_sha1(orig_head) && !is_cache_unborn())
> die(_("Updating an unborn branch with changes added 
> to the index."));
>
> -   git_config_get_bool("rebase.autostash", );
> -   if (!autostash)
> +   if(opt_autostash < 0)
> +   
> if(git_config_get_bool("rebase.autostash",_autostash))
> +   opt_autostash = 0;

I wonder if this code could be shortened if we simply just called
git_config_get_bool() just before parse_options(). That way, we don't
need to check for the "-1" special value.

> +
> +   if (!opt_autostash)
> die_on_unclean_work_tree(prefix);

OK.

>
> if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
> index c952d5e..512d3bf 100755
> --- a/t/t5520-pull.sh
> +++ b/t/t5520-pull.sh
> @@ -245,6 +245,14 @@ test_expect_success '--rebase fails with multiple 
> branches' '
> test modified = "$(git show HEAD:file)"
>  '
>
> +test_expect_success '--rebase --no-autostash fails with dirty working 
> directory' '

Maybe add ..."and rebase.autostash set" to the test name? Describes
the test better, and is consistent with the name of the test below.

> +   test_config rebase.autostash true &&
> +   git reset --hard before-rebase &&
> +   echo dirty >new_file &&
> +   git add new_file &&
> +   test_must_fail git pull --rebase --no-autostash . copy
> +'
> +
>  test_expect_success 'pull --rebase succeeds with dirty working directory and 
> rebase.autostash set' '
> test_config rebase.autostash true &&
> git reset --hard before-rebase &&
> diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
> index 18372ca..22ff5d7 100755
> --- a/t/t5521-pull-options.sh
> +++ b/t/t5521-pull-options.sh
> @@ -62,6 +62,30 @@ test_expect_success 'git pull -v --rebase' '
> test_must_be_empty out)
>  '
>
> +test_expect_success 'git pull --rebase --no-autostash' '
> +   mkdir clonedrbnas &&


Re: [GSoC] Microproject :- Teaching git pull --rebase the --no-autostash flag

2016-02-26 Thread Paul Tan
On Fri, Feb 26, 2016 at 7:23 PM, Mehul Jain  wrote:
> With this patch, git pull --rebase will understand --no-autostash command 
> line flag.
> This flag will override "rebase.autostash" configuration(if set) and leads to 
> a
> failure if current working directory is dirty. If "rebase.autostash" is not 
> configured
> and no flag is passed then by default command will die if directory is to be 
> dirty, before
> even calling rebase.
>
> I am also thinking of adding a "--autostash" flag for git pull --rebase, 
> which will
> override "rebase.autostash" configuration(if set false) and will pass 
> --autostash to
> git rebase. I would like to know your opinion on this option.

That was the point of the microproject ;-). --[no-]autostash means
both --autostash and --no-autostash.

Thanks,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 0/2] Reinstate the helpful message when `git pull --rebase` fails

2015-10-08 Thread Paul Tan
On Fri, Oct 9, 2015 at 8:52 AM, Junio C Hamano  wrote:
> Johannes Schindelin  writes:
>
>> Brendan Forster noticed that we no longer see the helpful message after
>> a failed `git pull --rebase`. It turns out that the builtin `am` calls
>> the recursive merge function directly, not via a separate process.
>>
>> But that function was not really safe to be called that way, as it
>> die()s pretty liberally.

I'm not too familiar with the merge-recursive.c code, but I was under
the impression that it only called die() under fatal conditions. In
common use cases, such as merge conflicts, it just errors out and the
helpful error message does get printed. Is there a reproduction recipe
for this?

That said, I do agree that even if we die(), we could try to be more
helpful by printing additional helpful instructions.

> If that is the case, I'd thinkg that we'd prefer, as a regression
> fix to correct "that", i.e., let recursive-merge die and let the
> caller catch its exit status.

We could do that, but I don't think it would be worth the overhead to
spawn an additional process for every patch just to print an
additional message should merge_recursive() call die().

Instead, stepping back a bit, I wonder if we can extend coverage of
the helpful message to all die() calls when running git-am. We could
just install a die routine with set_die_routine() in builtin/am.c.
Then, should die() be called anywhere, the helpful error message will
be printed as well. fast-import.c and http-backend.c seem to do this.

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] am: match --signoff to the original scripted version

2015-09-06 Thread Paul Tan
Hi,

Thanks for handling this.

On Sun, Sep 6, 2015 at 12:56 PM, Junio C Hamano  wrote:
> Linus noticed that the recently reimplementated "git am -s" defines

s/reimplementated/reimplemented/ ?

> the trailer block too rigidly, resulting an unnecessary blank line

s/resulting an/resulting in an/ ?

> between the existing sign-offs and his new sign-off.  An e-mail
> submission sent to Linus in real life ends with mixture of sign-offs
> and commentaries, e.g.
>
> title here
>
> message here
>
> Signed-off-by: Original Author 
> [rv: tweaked frotz and nitfol]
> Signed-off-by: Re Viewer 
> Signed-off-by: Other Reviewer 
> ---
> patch here
>
> Because the reimplementation reused append_signoff() helper that is
> used by other codepaths, which is unaware that people intermix such
> comments with their sign-offs in the trailer block, such a message
> was judged to end with a non-trailer, resulting in an extra blank

s/extra blank/extra blank line/ ?

> before adding a new sign-off.
>
> The original scripted version of "git am" used a lot looser
> definition, i.e. "if and only if there is no line that begins with
> Signed-off-by:, add a blank line before adding a new sign-off".  For
> the upcoming release, stop using the append_signoff() in "git am"
> and reimplement the looser definition used by the scripted version
> to use only in "git am" to fix this regression in "am" while
> avoiding new regressions to other users of append_signoff().
>
> In the longer term, we should look into loosening append_signoff()
> so that other codepaths that add a new sign-off behave the same way
> as "git am -s", but that is a task for post-release.
>
> Reported-by: Linus Torvalds 
> Signed-off-by: Junio C Hamano 
> ---
>  builtin/am.c  | 31 +--
>  t/t4150-am.sh | 48 
>  2 files changed, 77 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/am.c b/builtin/am.c
> index 634f7a7..e7828e5 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1191,6 +1191,33 @@ static void NORETURN die_user_resolve(const struct 
> am_state *state)
> exit(128);
>  }
>
> +static void am_signoff(struct strbuf *sb)
> +{

Hmm, okay, but now we have two similarly named functions am_signoff()
and am_append_signoff() which both do nearly similar things, the only
difference being am_signoff() operates on a strbuf while
am_append_signoff() operates on the "msg" char* field in the am_state,
which seems a bit iffy to me. I wonder if the logic could be
implemented in am_append_signoff() instead so we have only one
function?

> +   char *cp;
> +   struct strbuf mine = STRBUF_INIT;
> +
> +   /* Does it end with our own sign-off? */
> +   strbuf_addf(, "\n%s%s\n",
> +   sign_off_header,
> +   fmt_name(getenv("GIT_COMMITTER_NAME"),
> +getenv("GIT_COMMITTER_EMAIL")));

Maybe use git_committer_info() here?

> +   if (mine.len < sb->len &&
> +   !strcmp(mine.buf, sb->buf + sb->len - mine.len))

Perhaps use ends_with()?

> +   goto exit; /* no need to duplicate */
> +
> +   /* Does it have any Signed-off-by: in the text */
> +   for (cp = sb->buf;
> +cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
> +cp = strchr(cp, '\n')) {
> +   if (sb->buf == cp || cp[-1] == '\n')
> +   break;
> +   }
> +
> +   strbuf_addstr(sb, mine.buf + !!cp);
> +exit:
> +   strbuf_release();
> +}
> +
>  /**
>   * Appends signoff to the "msg" field of the am_state.
>   */
> @@ -1199,7 +1226,7 @@ static void am_append_signoff(struct am_state *state)
> struct strbuf sb = STRBUF_INIT;
>
> strbuf_attach(, state->msg, state->msg_len, state->msg_len);
> -   append_signoff(, 0, 0);
> +   am_signoff();
> state->msg = strbuf_detach(, >msg_len);
>  }
>
> @@ -1303,7 +1330,7 @@ static int parse_mail(struct am_state *state, const 
> char *mail)
> stripspace(, 0);
>
> if (state->signoff)
> -   append_signoff(, 0, 0);
> +   am_signoff();
>
> assert(!state->author_name);
> state->author_name = strbuf_detach(_name, NULL);

Thanks again,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] am: match --signoff to the original scripted version

2015-09-06 Thread Paul Tan
On Sun, Sep 6, 2015 at 12:56 PM, Junio C Hamano  wrote:
> diff --git a/builtin/am.c b/builtin/am.c
> index 634f7a7..e7828e5 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -1191,6 +1191,33 @@ static void NORETURN die_user_resolve(const struct 
> am_state *state)
> exit(128);
>  }
>
> +static void am_signoff(struct strbuf *sb)
> +{
> +   char *cp;
> +   struct strbuf mine = STRBUF_INIT;
> +
> +   /* Does it end with our own sign-off? */
> +   strbuf_addf(, "\n%s%s\n",
> +   sign_off_header,
> +   fmt_name(getenv("GIT_COMMITTER_NAME"),
> +getenv("GIT_COMMITTER_EMAIL")));
> +   if (mine.len < sb->len &&
> +   !strcmp(mine.buf, sb->buf + sb->len - mine.len))
> +   goto exit; /* no need to duplicate */
> +
> +   /* Does it have any Signed-off-by: in the text */
> +   for (cp = sb->buf;
> +cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
> +cp = strchr(cp, '\n')) {
> +   if (sb->buf == cp || cp[-1] == '\n')
> +   break;
> +   }
> +
> +   strbuf_addstr(sb, mine.buf + !!cp);

To add on, I wonder if the above "add a blank line if there is no
Signed-off-by: at the beginning of a line" logic could be expressed
more succinctly like this:

if (!starts_with(sb->buf, "Signed-off-by: ") &&
!strstr(sb->buf, "\nSigned-off-by: "))
strbuf_addch(sb, '\n');

strbuf_addstr(sb, mine.buf + 1);

> +exit:
> +   strbuf_release();
> +}
> +
>  /**
>   * Appends signoff to the "msg" field of the am_state.
>   */

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: GSoC 2015 is over

2015-09-02 Thread Paul Tan
On Wed, Sep 2, 2015 at 12:55 AM, Matthieu Moy
 wrote:
> I consider this GSoC as a great success and a pleasant experience.
> Congratulation to Paul and Karthik, and a warm "thank you" to everybody
> who contributed: administrators, mentors, reviewers, and obviously
> Junio! (not to mention Google, who made all this possible)
>
> Thanks all!

Thanks! The products of my project are not perfect (Jeff just fixed
another bug in the builtin am >< Thanks!), and there are still a few
leftover bits, but I hope that I've provided a good base of code for
working on.

Off the top of my head, the leftover bits are:

1. Stefan noticed that the help strings of git-am's options could be
more user-friendly:
http://thread.gmane.org/gmane.comp.version-control.git/272876/focus=273029

2. Johannes suggested that get_tracking_branch() in builtin/pull.c
could be implemented with remote_find_tracking():
http://thread.gmane.org/gmane.comp.version-control.git/269258/focus=269350

3. Junio noticed off-list that relative paths do not work with
git-fetch and git-pull

Other possible future developments:

1. git am --3way implemented on top of git apply --3way

2. builtin/mailinfo.c looks to me like a good candidate for moving
into libgit.a, so that git am can access its functionality without
spawning a separate process and writing temporary files.

On my part, I have to give a big thank you to Junio, as well as my
mentors Johannes and Stefan, for reviewing my patches. Their timely
review of my patches played a great part in getting my project into
master. Jeff, Eric and many others contributed by reporting bugs,
giving ideas and cleaning up my mess.

Above all, I would like to thank the organization admins as well as my
mentors for running the GSoC program. Thank you for this wonderful
opportunity. I've learned a lot, and had lots of fun.

I've really enjoyed my time with the Git community, and will stay
around for the foreseeable future.

Regards,
Paul
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: GSoC 2015 is over

2015-09-02 Thread Paul Tan
On Wed, Sep 2, 2015 at 1:43 AM, Karthik Nayak  wrote:
> On Tue, Sep 1, 2015 at 10:25 PM, Matthieu Moy
>  wrote:
>> Hi,
>>
>> The Google Summer of Code 2015 is officially over. We had two students
>> (Paul and Karthik), and both of them passed. 100 % success :-).
>>
>
> Congrats Paul :)

Thanks, you too ;-)
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] am: terminate state files with a newline

2015-08-22 Thread Paul Tan
On Thu, Aug 20, 2015 at 11:40:20AM -0700, Junio C Hamano wrote:
 SZEDER Gábor sze...@ira.uka.de writes:
 
  The format of the files '.git/rebase-apply/{next,last}' changed slightly
  with the recent builtin 'git am' conversion: while these files were
  newline-terminated when written by the scripted version, the ones written
  by the builtin are not.
 
 Thanks for noticing; that should be corrected, I think.

Okay then, this patch should correct this.

Did we ever explictly allow external programs to poke around the
contents of the .git/rebase-apply directory? I think it may not be so
good, as it means that it may not be possible to switch the storage
format in the future (e.g. to allow atomic modifications, maybe?) :-/ .

Regards,
Paul

-- 8 --
Subject: [PATCH] am: terminate state files with a newline

Since builtin/am.c replaced git-am.sh in 783d7e8 (builtin-am: remove
redirection to git-am.sh, 2015-08-04), the state files written by git-am
did not terminate with a newline.

This is because the code in builtin/am.c did not write the newline to
the state files.

While the git codebase has no problems with the missing newline,
external software which read the contents of the state directory may be
strict about the existence of the terminating newline, and would thus
break.

Fix this by correcting the relevant calls to write_file() to ensure that
the state files written terminate with a newline, matching how git-am.sh
behaves.

While we are fixing the write_file() calls, fix the writing of the
dirtyindex file as well -- we should be creating an empty file to
match the behavior of git-am.sh.

Reported-by: SZEDER Gábor sze...@ira.uka.de
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 30 +++---
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 1399c8d..2e57fad 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -994,13 +994,13 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
if (state-rebasing)
state-threeway = 1;
 
-   write_file(am_path(state, threeway), 1, state-threeway ? t : f);
+   write_file(am_path(state, threeway), 1, %s\n, state-threeway ? t 
: f);
 
-   write_file(am_path(state, quiet), 1, state-quiet ? t : f);
+   write_file(am_path(state, quiet), 1, %s\n, state-quiet ? t : 
f);
 
-   write_file(am_path(state, sign), 1, state-signoff ? t : f);
+   write_file(am_path(state, sign), 1, %s\n, state-signoff ? t : 
f);
 
-   write_file(am_path(state, utf8), 1, state-utf8 ? t : f);
+   write_file(am_path(state, utf8), 1, %s\n, state-utf8 ? t : f);
 
switch (state-keep) {
case KEEP_FALSE:
@@ -1016,9 +1016,9 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
die(BUG: invalid value for state-keep);
}
 
-   write_file(am_path(state, keep), 1, %s, str);
+   write_file(am_path(state, keep), 1, %s\n, str);
 
-   write_file(am_path(state, messageid), 1, state-message_id ? t : 
f);
+   write_file(am_path(state, messageid), 1, %s\n, state-message_id ? 
t : f);
 
switch (state-scissors) {
case SCISSORS_UNSET:
@@ -1034,10 +1034,10 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
die(BUG: invalid value for state-scissors);
}
 
-   write_file(am_path(state, scissors), 1, %s, str);
+   write_file(am_path(state, scissors), 1, %s\n, str);
 
sq_quote_argv(sb, state-git_apply_opts.argv, 0);
-   write_file(am_path(state, apply-opt), 1, %s, sb.buf);
+   write_file(am_path(state, apply-opt), 1, %s\n, sb.buf);
 
if (state-rebasing)
write_file(am_path(state, rebasing), 1, %s, );
@@ -1045,7 +1045,7 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
write_file(am_path(state, applying), 1, %s, );
 
if (!get_sha1(HEAD, curr_head)) {
-   write_file(am_path(state, abort-safety), 1, %s, 
sha1_to_hex(curr_head));
+   write_file(am_path(state, abort-safety), 1, %s\n, 
sha1_to_hex(curr_head));
if (!state-rebasing)
update_ref(am, ORIG_HEAD, curr_head, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
@@ -1060,9 +1060,9 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 * session is in progress, they should be written last.
 */
 
-   write_file(am_path(state, next), 1, %d, state-cur);
+   write_file(am_path(state, next), 1, %d\n, state-cur);
 
-   write_file(am_path(state, last), 1, %d, state-last);
+   write_file(am_path(state, last), 1, %d\n, state-last);
 
strbuf_release(sb);
 }
@@ -1095,12 +1095,12 @@ static void am_next(struct am_state *state)
unlink(am_path(state, original-commit));
 
if (!get_sha1(HEAD, head))
-   write_file

[PATCH v2] am --skip/--abort: merge HEAD/ORIG_HEAD tree into index

2015-08-19 Thread Paul Tan
On Mon, Aug 17, 2015 at 12:33:40PM -0700, Junio C Hamano wrote:
 Have you checked how this change affects that codepath?  To put it
 differently, does am skip have the same issue without this fix?

Hmm, I adopted Dscho's test to run git am --skip and it did not fail.
I think it's because am_skip() calls am_run(), which calls
refresh_cache(), so the resulting index will have the updated stat info.
However, there should still be a performance penalty because
refresh_cache() would have to scan all files for changes.

 If so, I wonder if we can have a test for that, too?

So yeah, we should have a test for that too.

(In addition, I fixed a small mistake with the struct tree_desc array
size.)

Thanks,
Paul

-- 8 --
Subject: [PATCH v2] am --skip/--abort: merge HEAD/ORIG_HEAD tree into index

After running git am --abort, and then running git reset --hard,
files that were not modified would still be re-checked out.

This is because clean_index() in builtin/am.c mistakenly called the
read_tree() function, which overwrites all entries in the index,
including the stat info.

git am --skip did not seem to have this issue because am_skip() called
am_run(), which called refresh_cache() to update the stat info. However,
there's still a performance penalty as the lack of stat info meant that
refresh_cache() would have to scan all files for changes.

Fix this by using unpack_trees() instead to merge the tree into the
index, so that the stat info from the index is kept.

Reported-by: Linus Torvalds torva...@linux-foundation.org
Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Johannes Schindelin johannes.schinde...@gmx.de
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c| 49 -
 t/t4151-am-abort.sh | 24 
 2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 1399c8d..3e7e66f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1940,15 +1940,48 @@ static int fast_forward_to(struct tree *head, struct 
tree *remote, int reset)
 }
 
 /**
+ * Merges a tree into the index. The index's stat info will take precedence
+ * over the merged tree's. Returns 0 on success, -1 on failure.
+ */
+static int merge_tree(struct tree *tree)
+{
+   struct lock_file *lock_file;
+   struct unpack_trees_options opts;
+   struct tree_desc t[1];
+
+   if (parse_tree(tree))
+   return -1;
+
+   lock_file = xcalloc(1, sizeof(struct lock_file));
+   hold_locked_index(lock_file, 1);
+
+   memset(opts, 0, sizeof(opts));
+   opts.head_idx = 1;
+   opts.src_index = the_index;
+   opts.dst_index = the_index;
+   opts.merge = 1;
+   opts.fn = oneway_merge;
+   init_tree_desc(t[0], tree-buffer, tree-size);
+
+   if (unpack_trees(1, t, opts)) {
+   rollback_lock_file(lock_file);
+   return -1;
+   }
+
+   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
+   die(_(unable to write new index file));
+
+   return 0;
+}
+
+/**
  * Clean the index without touching entries that are not modified between
  * `head` and `remote`.
  */
 static int clean_index(const unsigned char *head, const unsigned char *remote)
 {
-   struct lock_file *lock_file;
struct tree *head_tree, *remote_tree, *index_tree;
unsigned char index[GIT_SHA1_RAWSZ];
-   struct pathspec pathspec;
 
head_tree = parse_tree_indirect(head);
if (!head_tree)
@@ -1973,18 +2006,8 @@ static int clean_index(const unsigned char *head, const 
unsigned char *remote)
if (fast_forward_to(index_tree, remote_tree, 0))
return -1;
 
-   memset(pathspec, 0, sizeof(pathspec));
-
-   lock_file = xcalloc(1, sizeof(struct lock_file));
-   hold_locked_index(lock_file, 1);
-
-   if (read_tree(remote_tree, 0, pathspec)) {
-   rollback_lock_file(lock_file);
+   if (merge_tree(remote_tree))
return -1;
-   }
-
-   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
-   die(_(unable to write new index file));
 
remove_branch_state();
 
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 05bdc3e..ea5ace9 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -168,4 +168,28 @@ test_expect_success 'am --abort on unborn branch will keep 
local commits intact'
test_cmp expect actual
 '
 
+test_expect_success 'am --skip leaves index stat info alone' '
+   git checkout -f --orphan skip-stat-info 
+   git reset 
+   test_commit skip-should-be-untouched 
+   test-chmtime =0 skip-should-be-untouched.t 
+   git update-index --refresh 
+   git diff-files --exit-code --quiet 
+   test_must_fail git am 0001-*.patch 
+   git am --skip 
+   git diff-files --exit-code --quiet
+'
+
+test_expect_success 'am --abort leaves index stat info alone' '
+   git checkout -f --orphan abort

Re: [PATCH] git-am: add am.threeWay config variable

2015-08-19 Thread Paul Tan
On Tue, Aug 18, 2015 at 5:36 PM, Matthieu Moy
matthieu@grenoble-inp.fr wrote:
 I don't remember the details of the regression we had with the shell
 version, but that would probably deserve an additional test to enforce
 the Hopefully there will be no regressions part of your message.

Actually, technically, I think this patch by its own would reintroduce
the regression ;)

The reason is that the bug was caused by the overall structure of the
git-am.sh code, and not the patch itself[1].

This is fixed in another patch series[2] on top of this patch which
also implements a test for git am --3way.

[1] http://thread.gmane.org/gmane.comp.version-control.git/274577
[2] http://thread.gmane.org/gmane.comp.version-control.git/275322

Thanks,
Paul
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] am --abort: merge ORIG_HEAD tree into index

2015-08-17 Thread Paul Tan
On Mon, Aug 17, 2015 at 10:01:29AM +0200, Johannes Schindelin wrote:
 Hi Linus,
 
 On 2015-08-17 01:33, Linus Torvalds wrote:
  On Sun, Aug 16, 2015 at 12:46 PM, Linus Torvalds
  torva...@linux-foundation.org wrote:
 
  Maybe it has always done this, and I just haven't noticed (I usually
  _just_ do the git reset --hard thing, don't ask me why I wanted to
  be doubly sure this time). But maybe it's an effect of the new
  built-in am.
  
  I bisected this. It's definitely used to work, and the regression is
  from the new built-in am.
 
 This patch is a reproducer:

Thanks Johannes for the test, and sorry all for the oversight. 

It's true that we need to merge the ORIG_HEAD tree into the index
instead of overwriting it. Patch below.

Regards,
Paul

-- 8 --
Subject: [PATCH] am --abort: merge ORIG_HEAD tree into index

After running git am --abort, and then running git reset --hard,
files that were not modified would still be re-checked out.

This is because clean_index() in builtin/am.c mistakenly called the
read_tree() function, which overwrites all entries in the index,
including the stat info.

Fix this by using unpack_trees() instead to merge the tree into the
index, so that the stat info from the index is kept.

Reported-by: Linus Torvalds torva...@linux-foundation.org
Signed-off-by: Johannes Schindelin johannes.schinde...@gmx.de
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c| 49 -
 t/t4151-am-abort.sh | 12 
 2 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 1399c8d..6aaa85d 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1940,15 +1940,48 @@ static int fast_forward_to(struct tree *head, struct 
tree *remote, int reset)
 }
 
 /**
+ * Merges a tree into the index. The index's stat info will take precedence
+ * over the merged tree's. Returns 0 on success, -1 on failure.
+ */
+static int merge_tree(struct tree *tree)
+{
+   struct lock_file *lock_file;
+   struct unpack_trees_options opts;
+   struct tree_desc t[2];
+
+   if (parse_tree(tree))
+   return -1;
+
+   lock_file = xcalloc(1, sizeof(struct lock_file));
+   hold_locked_index(lock_file, 1);
+
+   memset(opts, 0, sizeof(opts));
+   opts.head_idx = 1;
+   opts.src_index = the_index;
+   opts.dst_index = the_index;
+   opts.merge = 1;
+   opts.fn = oneway_merge;
+   init_tree_desc(t[0], tree-buffer, tree-size);
+
+   if (unpack_trees(1, t, opts)) {
+   rollback_lock_file(lock_file);
+   return -1;
+   }
+
+   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
+   die(_(unable to write new index file));
+
+   return 0;
+}
+
+/**
  * Clean the index without touching entries that are not modified between
  * `head` and `remote`.
  */
 static int clean_index(const unsigned char *head, const unsigned char *remote)
 {
-   struct lock_file *lock_file;
struct tree *head_tree, *remote_tree, *index_tree;
unsigned char index[GIT_SHA1_RAWSZ];
-   struct pathspec pathspec;
 
head_tree = parse_tree_indirect(head);
if (!head_tree)
@@ -1973,18 +2006,8 @@ static int clean_index(const unsigned char *head, const 
unsigned char *remote)
if (fast_forward_to(index_tree, remote_tree, 0))
return -1;
 
-   memset(pathspec, 0, sizeof(pathspec));
-
-   lock_file = xcalloc(1, sizeof(struct lock_file));
-   hold_locked_index(lock_file, 1);
-
-   if (read_tree(remote_tree, 0, pathspec)) {
-   rollback_lock_file(lock_file);
+   if (merge_tree(remote_tree))
return -1;
-   }
-
-   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
-   die(_(unable to write new index file));
 
remove_branch_state();
 
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 05bdc3e..9c3bbd1 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -168,4 +168,16 @@ test_expect_success 'am --abort on unborn branch will keep 
local commits intact'
test_cmp expect actual
 '
 
+test_expect_success 'am --abort leaves index stat info alone' '
+   git checkout -f --orphan stat-info 
+   git reset 
+   test_commit should-be-untouched 
+   test-chmtime =0 should-be-untouched.t 
+   git update-index --refresh 
+   git diff-files --exit-code --quiet 
+   test_must_fail git am 0001-*.patch 
+   git am --abort 
+   git diff-files --exit-code --quiet
+'
+
 test_done
-- 
2.5.0.331.g11c07ce

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2 1/3] test_terminal: redirect child process' stdin to a pty

2015-08-11 Thread Paul Tan
On Fri, Aug 7, 2015 at 6:15 AM, Eric Sunshine sunsh...@sunshineco.com wrote:
 An alternative would be to have git-am detect that it is being tested
 and pretend that isatty() returns true.

I would vastly prefer a solution that would work for everything, for
all the C code and scripts, instead of implementing a workaround in
git-am :(

In this case, I implemented a generic solution in test-terminal.perl
that works for POSIX systems, so if there are no problems with its
implementation, I do think it's better. Other than the fact that it
does not work on non-Unix platforms, of course.

The other approach I would consider is to implement a xisatty()
function that returns true for xisatty(0) if TEST_TTY=0 or something.

However, I do wonder if this would lead us to have to hack around
other functions of terminals as well (e.g. if xisatty(0),
tcgetattr()), which would be a big can of worms I think...

 There is some precedent for
 having core functionality recognize that it is being tested. See, for
 instance, environment variable TEST_DATE_NOW,

(Hmm, I took a look, and it seems that TEST_DATE_NOW is only checked
in test-date.c...)

 and rev-list --test-bitmap.
 Doing so would allow the tests work on non-Unix
 platforms, as well.

Ehh, if the non-Unix platforms do not implement terminals, it means
that the git-am logic to detect if we are attempting to feed it a
patch by checking if stdin is a TTY is invalid anyway, so implementing
a yeah-it-is-a-tty workaround for the sake of tests would be hiding
the problem, I think.

Thanks,
Paul
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2 3/3] am: let --signoff override --no-signoff

2015-08-11 Thread Paul Tan
On Fri, Aug 7, 2015 at 5:29 PM, Johannes Schindelin
johannes.schinde...@gmx.de wrote:
 diff --git a/builtin/am.c b/builtin/am.c
 index 0961304..8c95aec 100644
 --- a/builtin/am.c
 +++ b/builtin/am.c
 @@ -2265,6 +2284,9 @@ int cmd_am(int argc, const char **argv, const
 char *prefix)

   if (resume == RESUME_FALSE)
   resume = RESUME_APPLY;
 +
 + if (state.signoff == SIGNOFF_EXPLICIT)
 + am_append_signoff(state);
   } else {

 This is clever, but I suspect there is now a chance for a double-signoff if 
 we passed `--signoff` to the initial `git am` call and it went through 
 without having to resume.

It's not present in this diff context, but this hunk modifies the code
path where in_progress is true. In other words, we only check for
SIGNOFF_EXPLICIT if
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v2 3/3] am: let --signoff override --no-signoff

2015-08-11 Thread Paul Tan
On Wed, Aug 12, 2015 at 11:06 AM, Paul Tan pyoka...@gmail.com wrote:
 It's not present in this diff context, but this hunk modifies the code
 path where in_progress is true. In other words, we only check for
 SIGNOFF_EXPLICIT if

..we are resuming.

(Ugh, butter fingers)

Thanks,
Paul
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 01/45] wrapper: implement xopen()

2015-08-04 Thread Paul Tan
A common usage pattern of open() is to check if it was successful, and
die() if it was not:

int fd = open(path, O_WRONLY | O_CREAT, 0777);
if (fd  0)
die_errno(_(Could not open '%s' for writing.), path);

Implement a wrapper function xopen() that does the above so that we can
save a few lines of code, and make the die() messages consistent.

Helped-by: Torsten Bögershausen tbo...@web.de
Helped-by: Jeff King p...@peff.net
Helped-by: Johannes Schindelin johannes.schinde...@gmx.de
Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 git-compat-util.h |  1 +
 wrapper.c | 35 +++
 2 files changed, 36 insertions(+)

diff --git a/git-compat-util.h b/git-compat-util.h
index c6d391f..e168dfd 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -717,6 +717,7 @@ extern void *xrealloc(void *ptr, size_t size);
 extern void *xcalloc(size_t nmemb, size_t size);
 extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, 
off_t offset);
 extern void *xmmap_gently(void *start, size_t length, int prot, int flags, int 
fd, off_t offset);
+extern int xopen(const char *path, int flags, ...);
 extern ssize_t xread(int fd, void *buf, size_t len);
 extern ssize_t xwrite(int fd, const void *buf, size_t len);
 extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
diff --git a/wrapper.c b/wrapper.c
index ff49807..0a4502d 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -189,6 +189,41 @@ void *xcalloc(size_t nmemb, size_t size)
 # endif
 #endif
 
+/**
+ * xopen() is the same as open(), but it die()s if the open() fails.
+ */
+int xopen(const char *path, int oflag, ...)
+{
+   mode_t mode = 0;
+   va_list ap;
+
+   /*
+* va_arg() will have undefined behavior if the specified type is not
+* compatible with the argument type. Since integers are promoted to
+* ints, we fetch the next argument as an int, and then cast it to a
+* mode_t to avoid undefined behavior.
+*/
+   va_start(ap, oflag);
+   if (oflag  O_CREAT)
+   mode = va_arg(ap, int);
+   va_end(ap);
+
+   for (;;) {
+   int fd = open(path, oflag, mode);
+   if (fd = 0)
+   return fd;
+   if (errno == EINTR)
+   continue;
+
+   if ((oflag  O_RDWR) == O_RDWR)
+   die_errno(_(could not open '%s' for reading and 
writing), path);
+   else if ((oflag  O_WRONLY) == O_WRONLY)
+   die_errno(_(could not open '%s' for writing), path);
+   else
+   die_errno(_(could not open '%s' for reading), path);
+   }
+}
+
 /*
  * xread() is the same a read(), but it automatically restarts read()
  * operations with a recoverable error (EAGAIN and EINTR). xread()
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 05/45] builtin-am: split out mbox/maildir patches with git-mailsplit

2015-08-04 Thread Paul Tan
git-am.sh supports mbox, stgit and mercurial patches. Re-implement
support for splitting out mbox/maildirs using git-mailsplit, while also
implementing the framework required to support other patch formats in
the future.

Re-implement support for the --patch-format option (since a5a6755
(git-am foreign patch support: introduce patch_format, 2009-05-27)) to
allow the user to choose between the different patch formats.

Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 107 +--
 1 file changed, 104 insertions(+), 3 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index ac172c4..5f3c131 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -8,6 +8,12 @@
 #include exec_cmd.h
 #include parse-options.h
 #include dir.h
+#include run-command.h
+
+enum patch_format {
+   PATCH_FORMAT_UNKNOWN = 0,
+   PATCH_FORMAT_MBOX
+};
 
 struct am_state {
/* state directory path */
@@ -16,6 +22,9 @@ struct am_state {
/* current and last patch numbers, 1-indexed */
int cur;
int last;
+
+   /* number of digits in patch filename */
+   int prec;
 };
 
 /**
@@ -28,6 +37,8 @@ static void am_state_init(struct am_state *state, const char 
*dir)
 
assert(dir);
state-dir = xstrdup(dir);
+
+   state-prec = 4;
 }
 
 /**
@@ -117,13 +128,71 @@ static void am_destroy(const struct am_state *state)
 }
 
 /**
+ * Splits out individual email patches from `paths`, where each path is either
+ * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
+ */
+static int split_mail_mbox(struct am_state *state, const char **paths)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+   struct strbuf last = STRBUF_INIT;
+
+   cp.git_cmd = 1;
+   argv_array_push(cp.args, mailsplit);
+   argv_array_pushf(cp.args, -d%d, state-prec);
+   argv_array_pushf(cp.args, -o%s, state-dir);
+   argv_array_push(cp.args, -b);
+   argv_array_push(cp.args, --);
+   argv_array_pushv(cp.args, paths);
+
+   if (capture_command(cp, last, 8))
+   return -1;
+
+   state-cur = 1;
+   state-last = strtol(last.buf, NULL, 10);
+
+   return 0;
+}
+
+/**
+ * Splits a list of files/directories into individual email patches. Each path
+ * in `paths` must be a file/directory that is formatted according to
+ * `patch_format`.
+ *
+ * Once split out, the individual email patches will be stored in the state
+ * directory, with each patch's filename being its index, padded to state-prec
+ * digits.
+ *
+ * state-cur will be set to the index of the first mail, and state-last will
+ * be set to the index of the last mail.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail(struct am_state *state, enum patch_format patch_format,
+   const char **paths)
+{
+   switch (patch_format) {
+   case PATCH_FORMAT_MBOX:
+   return split_mail_mbox(state, paths);
+   default:
+   die(BUG: invalid patch_format);
+   }
+   return -1;
+}
+
+/**
  * Setup a new am session for applying patches
  */
-static void am_setup(struct am_state *state)
+static void am_setup(struct am_state *state, enum patch_format patch_format,
+   const char **paths)
 {
if (mkdir(state-dir, 0777)  0  errno != EEXIST)
die_errno(_(failed to create directory '%s'), state-dir);
 
+   if (split_mail(state, patch_format, paths)  0) {
+   am_destroy(state);
+   die(_(Failed to split patches.));
+   }
+
/*
 * NOTE: Since the next and last files determine if an am_state
 * session is in progress, they should be written last.
@@ -159,9 +228,25 @@ static void am_run(struct am_state *state)
am_destroy(state);
 }
 
+/**
+ * parse_options() callback that validates and sets opt-value to the
+ * PATCH_FORMAT_* enum value corresponding to `arg`.
+ */
+static int parse_opt_patchformat(const struct option *opt, const char *arg, 
int unset)
+{
+   int *opt_value = opt-value;
+
+   if (!strcmp(arg, mbox))
+   *opt_value = PATCH_FORMAT_MBOX;
+   else
+   return error(_(Invalid value for --patch-format: %s), arg);
+   return 0;
+}
+
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
struct am_state state;
+   int patch_format = PATCH_FORMAT_UNKNOWN;
 
const char * const usage[] = {
N_(git am [options] [(mbox|Maildir)...]),
@@ -169,6 +254,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
};
 
struct option options[] = {
+   OPT_CALLBACK(0, patch-format, patch_format, N_(format),
+   N_(format the patch(es) are in),
+   parse_opt_patchformat),
OPT_END()
};
 
@@ -195,8 +283,21 @@ int cmd_am(int argc, const char **argv, const char

[PATCH v7 10/45] builtin-am: refuse to apply patches if index is dirty

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), git-am
will refuse to apply patches if the index is dirty. Re-implement this
behavior in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 45 +
 1 file changed, 45 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index a2811b6..537ad62 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -14,6 +14,8 @@
 #include cache-tree.h
 #include refs.h
 #include commit.h
+#include diff.h
+#include diffcore.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -565,6 +567,43 @@ static void refresh_and_write_cache(void)
 }
 
 /**
+ * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
+ * branch, returns 1 if there are entries in the index, 0 otherwise. If an
+ * strbuf is provided, the space-separated list of files that differ will be
+ * appended to it.
+ */
+static int index_has_changes(struct strbuf *sb)
+{
+   unsigned char head[GIT_SHA1_RAWSZ];
+   int i;
+
+   if (!get_sha1_tree(HEAD, head)) {
+   struct diff_options opt;
+
+   diff_setup(opt);
+   DIFF_OPT_SET(opt, EXIT_WITH_STATUS);
+   if (!sb)
+   DIFF_OPT_SET(opt, QUICK);
+   do_diff_cache(head, opt);
+   diffcore_std(opt);
+   for (i = 0; sb  i  diff_queued_diff.nr; i++) {
+   if (i)
+   strbuf_addch(sb, ' ');
+   strbuf_addstr(sb, diff_queued_diff.queue[i]-two-path);
+   }
+   diff_flush(opt);
+   return DIFF_OPT_TST(opt, HAS_CHANGES) != 0;
+   } else {
+   for (i = 0; sb  i  active_nr; i++) {
+   if (i)
+   strbuf_addch(sb, ' ');
+   strbuf_addstr(sb, active_cache[i]-name);
+   }
+   return !!active_nr;
+   }
+}
+
+/**
  * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
  * state-msg will be set to the patch message. state-author_name,
  * state-author_email and state-author_date will be set to the patch author's
@@ -726,9 +765,15 @@ static void do_commit(const struct am_state *state)
 static void am_run(struct am_state *state)
 {
const char *argv_gc_auto[] = {gc, --auto, NULL};
+   struct strbuf sb = STRBUF_INIT;
 
refresh_and_write_cache();
 
+   if (index_has_changes(sb))
+   die(_(Dirty index: cannot apply patches (dirty: %s)), sb.buf);
+
+   strbuf_release(sb);
+
while (state-cur = state-last) {
const char *mail = am_path(state, msgnum(state));
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 06/45] builtin-am: auto-detect mbox patches

2015-08-04 Thread Paul Tan
Since 15ced75 (git-am foreign patch support: autodetect some patch
formats, 2009-05-27), git-am.sh is able to autodetect mbox, stgit and
mercurial patches through heuristics.

Re-implement support for autodetecting mbox/maildir files in
builtin/am.c.

RFC 2822 requires that lines are terminated by \r\n. To support this,
implement strbuf_getline_crlf(), which will remove both '\n' and \r\n
from the end of the line.

Helped-by: Junio C Hamano gits...@pobox.com
Helped-by: Eric Sunshine sunsh...@sunshineco.com
Helped-by: Johannes Schindelin johannes.schinde...@gmx.de
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 109 +++
 1 file changed, 109 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 5f3c131..c12566a 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -10,6 +10,21 @@
 #include dir.h
 #include run-command.h
 
+/**
+ * Like strbuf_getline(), but treats both '\n' and \r\n as line terminators.
+ */
+static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp)
+{
+   if (strbuf_getwholeline(sb, fp, '\n'))
+   return EOF;
+   if (sb-buf[sb-len - 1] == '\n') {
+   strbuf_setlen(sb, sb-len - 1);
+   if (sb-len  0  sb-buf[sb-len - 1] == '\r')
+   strbuf_setlen(sb, sb-len - 1);
+   }
+   return 0;
+}
+
 enum patch_format {
PATCH_FORMAT_UNKNOWN = 0,
PATCH_FORMAT_MBOX
@@ -128,6 +143,92 @@ static void am_destroy(const struct am_state *state)
 }
 
 /**
+ * Determines if the file looks like a piece of RFC2822 mail by grabbing all
+ * non-indented lines and checking if they look like they begin with valid
+ * header field names.
+ *
+ * Returns 1 if the file looks like a piece of mail, 0 otherwise.
+ */
+static int is_mail(FILE *fp)
+{
+   const char *header_regex = ^[!-9;-~]+:;
+   struct strbuf sb = STRBUF_INIT;
+   regex_t regex;
+   int ret = 1;
+
+   if (fseek(fp, 0L, SEEK_SET))
+   die_errno(_(fseek failed));
+
+   if (regcomp(regex, header_regex, REG_NOSUB | REG_EXTENDED))
+   die(invalid pattern: %s, header_regex);
+
+   while (!strbuf_getline_crlf(sb, fp)) {
+   if (!sb.len)
+   break; /* End of header */
+
+   /* Ignore indented folded lines */
+   if (*sb.buf == '\t' || *sb.buf == ' ')
+   continue;
+
+   /* It's a header if it matches header_regex */
+   if (regexec(regex, sb.buf, 0, NULL, 0)) {
+   ret = 0;
+   goto done;
+   }
+   }
+
+done:
+   regfree(regex);
+   strbuf_release(sb);
+   return ret;
+}
+
+/**
+ * Attempts to detect the patch_format of the patches contained in `paths`,
+ * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if
+ * detection fails.
+ */
+static int detect_patch_format(const char **paths)
+{
+   enum patch_format ret = PATCH_FORMAT_UNKNOWN;
+   struct strbuf l1 = STRBUF_INIT;
+   FILE *fp;
+
+   /*
+* We default to mbox format if input is from stdin and for directories
+*/
+   if (!*paths || !strcmp(*paths, -) || is_directory(*paths))
+   return PATCH_FORMAT_MBOX;
+
+   /*
+* Otherwise, check the first few lines of the first patch, starting
+* from the first non-blank line, to try to detect its format.
+*/
+
+   fp = xfopen(*paths, r);
+
+   while (!strbuf_getline_crlf(l1, fp)) {
+   if (l1.len)
+   break;
+   }
+
+   if (starts_with(l1.buf, From ) || starts_with(l1.buf, From: )) {
+   ret = PATCH_FORMAT_MBOX;
+   goto done;
+   }
+
+   if (l1.len  is_mail(fp)) {
+   ret = PATCH_FORMAT_MBOX;
+   goto done;
+   }
+
+done:
+   fclose(fp);
+   strbuf_release(l1);
+   return ret;
+}
+
+/**
  * Splits out individual email patches from `paths`, where each path is either
  * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
  */
@@ -185,6 +286,14 @@ static int split_mail(struct am_state *state, enum 
patch_format patch_format,
 static void am_setup(struct am_state *state, enum patch_format patch_format,
const char **paths)
 {
+   if (!patch_format)
+   patch_format = detect_patch_format(paths);
+
+   if (!patch_format) {
+   fprintf_ln(stderr, _(Patch format detection failed.));
+   exit(128);
+   }
+
if (mkdir(state-dir, 0777)  0  errno != EEXIST)
die_errno(_(failed to create directory '%s'), state-dir);
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 02/45] wrapper: implement xfopen()

2015-08-04 Thread Paul Tan
A common usage pattern of fopen() is to check if it succeeded, and die()
if it failed:

FILE *fp = fopen(path, w);
if (!fp)
die_errno(_(could not open '%s' for writing), path);

Implement a wrapper function xfopen() for the above, so that we can save
a few lines of code and make the die() messages consistent.

Helped-by: Jeff King p...@peff.net
Helped-by: Johannes Schindelin johannes.schinde...@gmx.de
Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 git-compat-util.h |  1 +
 wrapper.c | 21 +
 2 files changed, 22 insertions(+)

diff --git a/git-compat-util.h b/git-compat-util.h
index e168dfd..392da79 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -722,6 +722,7 @@ extern ssize_t xread(int fd, void *buf, size_t len);
 extern ssize_t xwrite(int fd, const void *buf, size_t len);
 extern ssize_t xpread(int fd, void *buf, size_t len, off_t offset);
 extern int xdup(int fd);
+extern FILE *xfopen(const char *path, const char *mode);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
 extern int xmkstemp_mode(char *template, int mode);
diff --git a/wrapper.c b/wrapper.c
index 0a4502d..e451463 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -346,6 +346,27 @@ int xdup(int fd)
return ret;
 }
 
+/**
+ * xfopen() is the same as fopen(), but it die()s if the fopen() fails.
+ */
+FILE *xfopen(const char *path, const char *mode)
+{
+   for (;;) {
+   FILE *fp = fopen(path, mode);
+   if (fp)
+   return fp;
+   if (errno == EINTR)
+   continue;
+
+   if (*mode  mode[1] == '+')
+   die_errno(_(could not open '%s' for reading and 
writing), path);
+   else if (*mode == 'w' || *mode == 'a')
+   die_errno(_(could not open '%s' for writing), path);
+   else
+   die_errno(_(could not open '%s' for reading), path);
+   }
+}
+
 FILE *xfdopen(int fd, const char *mode)
 {
FILE *stream = fdopen(fd, mode);
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 08/45] builtin-am: apply patch with git-apply

2015-08-04 Thread Paul Tan
Implement applying the patch to the index using git-apply.

If a file is unchanged but stat-dirty, git-apply may erroneously fail to
apply patches, thinking that they conflict with a dirty working tree.

As such, since 2a6f08a (am: refresh the index at start and --resolved,
2011-08-15), git-am will refresh the index before applying patches.
Re-implement this behavior.

Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 72 +++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 63f0fa4..1f198e4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -10,6 +10,7 @@
 #include dir.h
 #include run-command.h
 #include quote.h
+#include lockfile.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -42,6 +43,14 @@ static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp)
return 0;
 }
 
+/**
+ * Returns the length of the first line of msg.
+ */
+static int linelen(const char *msg)
+{
+   return strchrnul(msg, '\n') - msg;
+}
+
 enum patch_format {
PATCH_FORMAT_UNKNOWN = 0,
PATCH_FORMAT_MBOX
@@ -540,6 +549,19 @@ static const char *msgnum(const struct am_state *state)
 }
 
 /**
+ * Refresh and write index.
+ */
+static void refresh_and_write_cache(void)
+{
+   struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+   hold_locked_index(lock_file, 1);
+   refresh_cache(REFRESH_QUIET);
+   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
+   die(_(unable to write index file));
+}
+
+/**
  * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
  * state-msg will be set to the patch message. state-author_name,
  * state-author_email and state-author_date will be set to the patch author's
@@ -629,10 +651,35 @@ finish:
 }
 
 /**
+ * Applies current patch with git-apply. Returns 0 on success, -1 otherwise.
+ */
+static int run_apply(const struct am_state *state)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+
+   cp.git_cmd = 1;
+
+   argv_array_push(cp.args, apply);
+   argv_array_push(cp.args, --index);
+   argv_array_push(cp.args, am_path(state, patch));
+
+   if (run_command(cp))
+   return -1;
+
+   /* Reload index as git-apply will have modified it. */
+   discard_cache();
+   read_cache();
+
+   return 0;
+}
+
+/**
  * Applies all queued mail.
  */
 static void am_run(struct am_state *state)
 {
+   refresh_and_write_cache();
+
while (state-cur = state-last) {
const char *mail = am_path(state, msgnum(state));
 
@@ -645,7 +692,27 @@ static void am_run(struct am_state *state)
write_author_script(state);
write_commit_msg(state);
 
-   /* NEEDSWORK: Patch application not implemented yet */
+   printf_ln(_(Applying: %.*s), linelen(state-msg), state-msg);
+
+   if (run_apply(state)  0) {
+   int advice_amworkdir = 1;
+
+   printf_ln(_(Patch failed at %s %.*s), msgnum(state),
+   linelen(state-msg), state-msg);
+
+   git_config_get_bool(advice.amworkdir, 
advice_amworkdir);
+
+   if (advice_amworkdir)
+   printf_ln(_(The copy of the patch that failed 
is found in: %s),
+   am_path(state, patch));
+
+   exit(128);
+   }
+
+   /*
+* NEEDSWORK: After the patch has been applied to the index
+* with git-apply, we need to make commit as well.
+*/
 
 next:
am_next(state);
@@ -707,6 +774,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
argc = parse_options(argc, argv, prefix, options, usage, 0);
 
+   if (read_index_preload(the_index, NULL)  0)
+   die(_(failed to read the index));
+
if (am_in_progress(state))
am_load(state);
else {
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 14/45] builtin-am: implement --abort

2015-08-04 Thread Paul Tan
Since 3e5057a (git am --abort, 2008-07-16), git-am supported the --abort
option that will rewind HEAD back to the original commit. Re-implement
this through am_abort().

Since 7b3b7e3 (am --abort: keep unrelated commits since the last failure
and warn, 2010-12-21), to prevent commits made since the last failure
from being lost, git-am will not rewind HEAD back to the original
commit if HEAD moved since the last failure. Re-implement this through
safe_to_abort().

Helped-by: Stefan Beller sbel...@google.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---

Notes:
v7

* Add a free(curr_branch) so we don't leak memory.

 builtin/am.c | 103 +--
 1 file changed, 100 insertions(+), 3 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 765844b..6c24d07 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -490,6 +490,8 @@ static int split_mail(struct am_state *state, enum 
patch_format patch_format,
 static void am_setup(struct am_state *state, enum patch_format patch_format,
const char **paths)
 {
+   unsigned char curr_head[GIT_SHA1_RAWSZ];
+
if (!patch_format)
patch_format = detect_patch_format(paths);
 
@@ -506,6 +508,14 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
die(_(Failed to split patches.));
}
 
+   if (!get_sha1(HEAD, curr_head)) {
+   write_file(am_path(state, abort-safety), 1, %s, 
sha1_to_hex(curr_head));
+   update_ref(am, ORIG_HEAD, curr_head, NULL, 0, 
UPDATE_REFS_DIE_ON_ERR);
+   } else {
+   write_file(am_path(state, abort-safety), 1, %s, );
+   delete_ref(ORIG_HEAD, NULL, 0);
+   }
+
/*
 * NOTE: Since the next and last files determine if an am_state
 * session is in progress, they should be written last.
@@ -522,6 +532,8 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
  */
 static void am_next(struct am_state *state)
 {
+   unsigned char head[GIT_SHA1_RAWSZ];
+
free(state-author_name);
state-author_name = NULL;
 
@@ -538,6 +550,11 @@ static void am_next(struct am_state *state)
unlink(am_path(state, author-script));
unlink(am_path(state, final-commit));
 
+   if (!get_sha1(HEAD, head))
+   write_file(am_path(state, abort-safety), 1, %s, 
sha1_to_hex(head));
+   else
+   write_file(am_path(state, abort-safety), 1, %s, );
+
state-cur++;
write_file(am_path(state, next), 1, %d, state-cur);
 }
@@ -788,10 +805,14 @@ static void am_run(struct am_state *state, int resume)
const char *argv_gc_auto[] = {gc, --auto, NULL};
struct strbuf sb = STRBUF_INIT;
 
+   unlink(am_path(state, dirtyindex));
+
refresh_and_write_cache();
 
-   if (index_has_changes(sb))
+   if (index_has_changes(sb)) {
+   write_file(am_path(state, dirtyindex), 1, t);
die(_(Dirty index: cannot apply patches (dirty: %s)), sb.buf);
+   }
 
strbuf_release(sb);
 
@@ -980,6 +1001,75 @@ static void am_skip(struct am_state *state)
 }
 
 /**
+ * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.
+ *
+ * It is not safe to reset HEAD when:
+ * 1. git-am previously failed because the index was dirty.
+ * 2. HEAD has moved since git-am previously failed.
+ */
+static int safe_to_abort(const struct am_state *state)
+{
+   struct strbuf sb = STRBUF_INIT;
+   unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];
+
+   if (file_exists(am_path(state, dirtyindex)))
+   return 0;
+
+   if (read_state_file(sb, state, abort-safety, 1)  0) {
+   if (get_sha1_hex(sb.buf, abort_safety))
+   die(_(could not parse %s), am_path(state, 
abort_safety));
+   } else
+   hashclr(abort_safety);
+
+   if (get_sha1(HEAD, head))
+   hashclr(head);
+
+   if (!hashcmp(head, abort_safety))
+   return 1;
+
+   error(_(You seem to have moved HEAD since the last 'am' failure.\n
+   Not rewinding to ORIG_HEAD));
+
+   return 0;
+}
+
+/**
+ * Aborts the current am session if it is safe to do so.
+ */
+static void am_abort(struct am_state *state)
+{
+   unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];
+   int has_curr_head, has_orig_head;
+   char *curr_branch;
+
+   if (!safe_to_abort(state)) {
+   am_destroy(state);
+   return;
+   }
+
+   curr_branch = resolve_refdup(HEAD, 0, curr_head, NULL);
+   has_curr_head = !is_null_sha1(curr_head);
+   if (!has_curr_head)
+   hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);
+
+   has_orig_head = !get_sha1(ORIG_HEAD, orig_head);
+   if (!has_orig_head)
+   hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);
+
+   clean_index(curr_head

[PATCH v7 13/45] builtin-am: implement --skip

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), git-am
supported resuming from a failed patch application by skipping the
current patch. Re-implement this feature by introducing am_skip().

Helped-by: Stefan Beller sbel...@google.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 123 ++-
 1 file changed, 121 insertions(+), 2 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index ec579a6..765844b 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -16,6 +16,8 @@
 #include commit.h
 #include diff.h
 #include diffcore.h
+#include unpack-trees.h
+#include branch.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -868,6 +870,116 @@ static void am_resolve(struct am_state *state)
 }
 
 /**
+ * Performs a checkout fast-forward from `head` to `remote`. If `reset` is
+ * true, any unmerged entries will be discarded. Returns 0 on success, -1 on
+ * failure.
+ */
+static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
+{
+   struct lock_file *lock_file;
+   struct unpack_trees_options opts;
+   struct tree_desc t[2];
+
+   if (parse_tree(head) || parse_tree(remote))
+   return -1;
+
+   lock_file = xcalloc(1, sizeof(struct lock_file));
+   hold_locked_index(lock_file, 1);
+
+   refresh_cache(REFRESH_QUIET);
+
+   memset(opts, 0, sizeof(opts));
+   opts.head_idx = 1;
+   opts.src_index = the_index;
+   opts.dst_index = the_index;
+   opts.update = 1;
+   opts.merge = 1;
+   opts.reset = reset;
+   opts.fn = twoway_merge;
+   init_tree_desc(t[0], head-buffer, head-size);
+   init_tree_desc(t[1], remote-buffer, remote-size);
+
+   if (unpack_trees(2, t, opts)) {
+   rollback_lock_file(lock_file);
+   return -1;
+   }
+
+   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
+   die(_(unable to write new index file));
+
+   return 0;
+}
+
+/**
+ * Clean the index without touching entries that are not modified between
+ * `head` and `remote`.
+ */
+static int clean_index(const unsigned char *head, const unsigned char *remote)
+{
+   struct lock_file *lock_file;
+   struct tree *head_tree, *remote_tree, *index_tree;
+   unsigned char index[GIT_SHA1_RAWSZ];
+   struct pathspec pathspec;
+
+   head_tree = parse_tree_indirect(head);
+   if (!head_tree)
+   return error(_(Could not parse object '%s'.), 
sha1_to_hex(head));
+
+   remote_tree = parse_tree_indirect(remote);
+   if (!remote_tree)
+   return error(_(Could not parse object '%s'.), 
sha1_to_hex(remote));
+
+   read_cache_unmerged();
+
+   if (fast_forward_to(head_tree, head_tree, 1))
+   return -1;
+
+   if (write_cache_as_tree(index, 0, NULL))
+   return -1;
+
+   index_tree = parse_tree_indirect(index);
+   if (!index_tree)
+   return error(_(Could not parse object '%s'.), 
sha1_to_hex(index));
+
+   if (fast_forward_to(index_tree, remote_tree, 0))
+   return -1;
+
+   memset(pathspec, 0, sizeof(pathspec));
+
+   lock_file = xcalloc(1, sizeof(struct lock_file));
+   hold_locked_index(lock_file, 1);
+
+   if (read_tree(remote_tree, 0, pathspec)) {
+   rollback_lock_file(lock_file);
+   return -1;
+   }
+
+   if (write_locked_index(the_index, lock_file, COMMIT_LOCK))
+   die(_(unable to write new index file));
+
+   remove_branch_state();
+
+   return 0;
+}
+
+/**
+ * Resume the current am session by skipping the current patch.
+ */
+static void am_skip(struct am_state *state)
+{
+   unsigned char head[GIT_SHA1_RAWSZ];
+
+   if (get_sha1(HEAD, head))
+   hashcpy(head, EMPTY_TREE_SHA1_BIN);
+
+   if (clean_index(head, head))
+   die(_(failed to clean index));
+
+   am_next(state);
+   am_run(state, 0);
+}
+
+/**
  * parse_options() callback that validates and sets opt-value to the
  * PATCH_FORMAT_* enum value corresponding to `arg`.
  */
@@ -885,7 +997,8 @@ static int parse_opt_patchformat(const struct option *opt, 
const char *arg, int
 enum resume_mode {
RESUME_FALSE = 0,
RESUME_APPLY,
-   RESUME_RESOLVED
+   RESUME_RESOLVED,
+   RESUME_SKIP
 };
 
 int cmd_am(int argc, const char **argv, const char *prefix)
@@ -896,7 +1009,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
const char * const usage[] = {
N_(git am [options] [(mbox|Maildir)...]),
-   N_(git am [options] --continue),
+   N_(git am [options] (--continue | --skip)),
NULL
};
 
@@ -910,6 +1023,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
OPT_CMDMODE('r', resolved, resume,
N_(synonyms for --continue

[PATCH v7 22/45] builtin-am: bypass git-mailinfo when --rebasing

2015-08-04 Thread Paul Tan
Since 5e835ca (rebase: do not munge commit log message, 2008-04-16),
git am --rebasing no longer gets the commit log message from the patch,
but reads it directly from the commit identified by the From  header
line.

Since 43c2325 (am: use get_author_ident_from_commit instead of mailinfo
when rebasing, 2010-06-16), git am --rebasing also gets the author name,
email and date directly from the commit.

Since 0fbb95d (am: don't call mailinfo if $rebasing, 2012-06-26), git am
--rebasing does not use git-mailinfo to get the patch body, but rather
generates it directly from the commit itself.

The above 3 commits introduced a separate parse_mail() code path in
git-am.sh's --rebasing mode that bypasses git-mailinfo. Re-implement
this code path in builtin/am.c as parse_mail_rebase().

Signed-off-by: Paul Tan pyoka...@gmail.com
---

Notes:
v7

* Since a5481a6 (convert enum date_mode into a struct, 2015-06-25),
  show_ident_date() now takes a date_mode struct. Use the DATE_MODE()
  macro to pass the equivalent date_mode struct to show_ident_date().

 builtin/am.c | 134 ++-
 1 file changed, 133 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 440a653..a02c84e 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -21,6 +21,8 @@
 #include sequencer.h
 #include revision.h
 #include merge-recursive.h
+#include revision.h
+#include log-tree.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -788,6 +790,129 @@ finish:
 }
 
 /**
+ * Sets commit_id to the commit hash where the mail was generated from.
+ * Returns 0 on success, -1 on failure.
+ */
+static int get_mail_commit_sha1(unsigned char *commit_id, const char *mail)
+{
+   struct strbuf sb = STRBUF_INIT;
+   FILE *fp = xfopen(mail, r);
+   const char *x;
+
+   if (strbuf_getline(sb, fp, '\n'))
+   return -1;
+
+   if (!skip_prefix(sb.buf, From , x))
+   return -1;
+
+   if (get_sha1_hex(x, commit_id)  0)
+   return -1;
+
+   strbuf_release(sb);
+   fclose(fp);
+   return 0;
+}
+
+/**
+ * Sets state-msg, state-author_name, state-author_email, state-author_date
+ * to the commit's respective info.
+ */
+static void get_commit_info(struct am_state *state, struct commit *commit)
+{
+   const char *buffer, *ident_line, *author_date, *msg;
+   size_t ident_len;
+   struct ident_split ident_split;
+   struct strbuf sb = STRBUF_INIT;
+
+   buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
+
+   ident_line = find_commit_header(buffer, author, ident_len);
+
+   if (split_ident_line(ident_split, ident_line, ident_len)  0) {
+   strbuf_add(sb, ident_line, ident_len);
+   die(_(invalid ident line: %s), sb.buf);
+   }
+
+   assert(!state-author_name);
+   if (ident_split.name_begin) {
+   strbuf_add(sb, ident_split.name_begin,
+   ident_split.name_end - ident_split.name_begin);
+   state-author_name = strbuf_detach(sb, NULL);
+   } else
+   state-author_name = xstrdup();
+
+   assert(!state-author_email);
+   if (ident_split.mail_begin) {
+   strbuf_add(sb, ident_split.mail_begin,
+   ident_split.mail_end - ident_split.mail_begin);
+   state-author_email = strbuf_detach(sb, NULL);
+   } else
+   state-author_email = xstrdup();
+
+   author_date = show_ident_date(ident_split, DATE_MODE(NORMAL));
+   strbuf_addstr(sb, author_date);
+   assert(!state-author_date);
+   state-author_date = strbuf_detach(sb, NULL);
+
+   assert(!state-msg);
+   msg = strstr(buffer, \n\n);
+   if (!msg)
+   die(_(unable to parse commit %s), 
sha1_to_hex(commit-object.sha1));
+   state-msg = xstrdup(msg + 2);
+   state-msg_len = strlen(state-msg);
+}
+
+/**
+ * Writes `commit` as a patch to the state directory's patch file.
+ */
+static void write_commit_patch(const struct am_state *state, struct commit 
*commit)
+{
+   struct rev_info rev_info;
+   FILE *fp;
+
+   fp = xfopen(am_path(state, patch), w);
+   init_revisions(rev_info, NULL);
+   rev_info.diff = 1;
+   rev_info.abbrev = 0;
+   rev_info.disable_stdin = 1;
+   rev_info.show_root_diff = 1;
+   rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+   rev_info.no_commit_id = 1;
+   DIFF_OPT_SET(rev_info.diffopt, BINARY);
+   DIFF_OPT_SET(rev_info.diffopt, FULL_INDEX);
+   rev_info.diffopt.use_color = 0;
+   rev_info.diffopt.file = fp;
+   rev_info.diffopt.close_file = 1;
+   add_pending_object(rev_info, commit-object, );
+   diff_setup_done(rev_info.diffopt);
+   log_tree_commit(rev_info, commit);
+}
+
+/**
+ * Like parse_mail(), but parses the mail by looking up its commit ID
+ * directly. This is used in --rebasing mode

[PATCH v7 36/45] builtin-am: invoke pre-applypatch hook

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sg will invoke the pre-applypatch hook after applying the patch
to the index, but before a commit is made. Should the hook exit with a
non-zero status, git am will exit.

Re-implement this in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index f0e3aab..7a7da94 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1334,6 +1334,9 @@ static void do_commit(const struct am_state *state)
const char *reflog_msg, *author;
struct strbuf sb = STRBUF_INIT;
 
+   if (run_hook_le(NULL, pre-applypatch, NULL))
+   exit(1);
+
if (write_cache_as_tree(tree, 0, NULL))
die(_(git write-tree failed to write a tree));
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 23/45] builtin-am: handle stray state directory

2015-08-04 Thread Paul Tan
Should git-am terminate unexpectedly between the point where the state
directory is created, but the next and last files are not written
yet, a stray state directory will be left behind.

As such, since b141f3c (am: handle stray $dotest directory, 2013-06-15),
git-am.sh explicitly recognizes such a stray directory, and allows the
user to remove it with am --abort.

Re-implement this feature in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 17 +
 1 file changed, 17 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index a02c84e..47dd4c7 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1530,6 +1530,23 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
struct argv_array paths = ARGV_ARRAY_INIT;
int i;
 
+   /*
+* Handle stray state directory in the independent-run case. In
+* the --rebasing case, it is up to the caller to take care of
+* stray directories.
+*/
+   if (file_exists(state.dir)  !state.rebasing) {
+   if (resume == RESUME_ABORT) {
+   am_destroy(state);
+   am_state_release(state);
+   return 0;
+   }
+
+   die(_(Stray %s directory found.\n
+   Use \git am --abort\ to remove it.),
+   state.dir);
+   }
+
if (resume)
die(_(Resolve operation not in progress, we are not 
resuming.));
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 37/45] builtin-am: invoke post-applypatch hook

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sh will invoke the post-applypatch hook after the patch is
applied and a commit is made. The exit code of the hook is ignored.

Re-implement this in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 7a7da94..c313e58 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1378,6 +1378,8 @@ static void do_commit(const struct am_state *state)
fclose(fp);
}
 
+   run_hook_le(NULL, post-applypatch, NULL);
+
strbuf_release(sb);
 }
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 34/45] builtin-am: support automatic notes copying

2015-08-04 Thread Paul Tan
Since eb2151b (rebase: support automatic notes copying, 2010-03-12),
git-am.sh supported automatic notes copying in --rebasing mode by
invoking git notes copy once it has finished applying all the patches.

Re-implement this feature in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 60 
 1 file changed, 60 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index dbec9fc..7d7f91d 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -23,6 +23,7 @@
 #include merge-recursive.h
 #include revision.h
 #include log-tree.h
+#include notes-utils.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -479,6 +480,64 @@ static int run_post_rewrite_hook(const struct am_state 
*state)
 }
 
 /**
+ * Reads the state directory's rewritten file, and copies notes from the old
+ * commits listed in the file to their rewritten commits.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int copy_notes_for_rebase(const struct am_state *state)
+{
+   struct notes_rewrite_cfg *c;
+   struct strbuf sb = STRBUF_INIT;
+   const char *invalid_line = _(Malformed input line: '%s'.);
+   const char *msg = Notes added by 'git rebase';
+   FILE *fp;
+   int ret = 0;
+
+   assert(state-rebasing);
+
+   c = init_copy_notes_for_rewrite(rebase);
+   if (!c)
+   return 0;
+
+   fp = xfopen(am_path(state, rewritten), r);
+
+   while (!strbuf_getline(sb, fp, '\n')) {
+   unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ];
+
+   if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) {
+   ret = error(invalid_line, sb.buf);
+   goto finish;
+   }
+
+   if (get_sha1_hex(sb.buf, from_obj)) {
+   ret = error(invalid_line, sb.buf);
+   goto finish;
+   }
+
+   if (sb.buf[GIT_SHA1_HEXSZ] != ' ') {
+   ret = error(invalid_line, sb.buf);
+   goto finish;
+   }
+
+   if (get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ + 1, to_obj)) {
+   ret = error(invalid_line, sb.buf);
+   goto finish;
+   }
+
+   if (copy_note_for_rewrite(c, from_obj, to_obj))
+   ret = error(_(Failed to copy notes from '%s' to '%s'),
+   sha1_to_hex(from_obj), 
sha1_to_hex(to_obj));
+   }
+
+finish:
+   finish_copy_notes_for_rewrite(c, msg);
+   fclose(fp);
+   strbuf_release(sb);
+   return ret;
+}
+
+/**
  * Determines if the file looks like a piece of RFC2822 mail by grabbing all
  * non-indented lines and checking if they look like they begin with valid
  * header field names.
@@ -1405,6 +1464,7 @@ next:
 
if (!is_empty_file(am_path(state, rewritten))) {
assert(state-rebasing);
+   copy_notes_for_rebase(state);
run_post_rewrite_hook(state);
}
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 30/45] builtin-am: implement --ignore-date

2015-08-04 Thread Paul Tan
Since a79ec62 (git-am: Add --ignore-date option, 2009-01-24), git-am.sh
supported the --ignore-date option, and would use the current timestamp
instead of the one provided in the patch if the option was set.

Re-implement this option in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index f842f69..84d3e05 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -108,6 +108,7 @@ struct am_state {
int scissors; /* enum scissors_type */
struct argv_array git_apply_opts;
const char *resolvemsg;
+   int ignore_date;
int rebasing;
 };
 
@@ -1217,7 +1218,8 @@ static void do_commit(const struct am_state *state)
}
 
author = fmt_ident(state-author_name, state-author_email,
-   state-author_date, IDENT_STRICT);
+   state-ignore_date ? NULL : state-author_date,
+   IDENT_STRICT);
 
if (commit_tree(state-msg, state-msg_len, tree, parents, commit,
author, NULL))
@@ -1661,6 +1663,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT_CMDMODE(0, abort, resume,
N_(restore the original branch and abort the patching 
operation.),
RESUME_ABORT),
+   OPT_BOOL(0, ignore-date, state.ignore_date,
+   N_(use current timestamp for author date)),
OPT_HIDDEN_BOOL(0, rebasing, state.rebasing,
N_((internal use for git-rebase))),
OPT_END()
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 21/45] builtin-am: implement --rebasing mode

2015-08-04 Thread Paul Tan
Since 3041c32 (am: --rebasing, 2008-03-04), git-am.sh supported the
--rebasing option, which is used internally by git-rebase to tell git-am
that it is being used for its purpose. It would create the empty file
$state_dir/rebasing to help completion scripts tell if the ongoing
operation is am or rebase.

As of 0fbb95d (am: don't call mailinfo if $rebasing, 2012-06-26),
--rebasing also implies --3way as well.

Since a1549e1 (am: return control to caller, for housekeeping,
2013-05-12), git-am.sh would only clean up the state directory when it
is not --rebasing, instead deferring cleanup to git-rebase.sh.

Re-implement the above in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 30 ++
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index a5d5e8c..440a653 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -89,6 +89,7 @@ struct am_state {
int quiet;
int signoff;
const char *resolvemsg;
+   int rebasing;
 };
 
 /**
@@ -364,6 +365,8 @@ static void am_load(struct am_state *state)
read_state_file(sb, state, sign, 1);
state-signoff = !strcmp(sb.buf, t);
 
+   state-rebasing = !!file_exists(am_path(state, rebasing));
+
strbuf_release(sb);
 }
 
@@ -542,18 +545,29 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
die(_(Failed to split patches.));
}
 
+   if (state-rebasing)
+   state-threeway = 1;
+
write_file(am_path(state, threeway), 1, state-threeway ? t : f);
 
write_file(am_path(state, quiet), 1, state-quiet ? t : f);
 
write_file(am_path(state, sign), 1, state-signoff ? t : f);
 
+   if (state-rebasing)
+   write_file(am_path(state, rebasing), 1, %s, );
+   else
+   write_file(am_path(state, applying), 1, %s, );
+
if (!get_sha1(HEAD, curr_head)) {
write_file(am_path(state, abort-safety), 1, %s, 
sha1_to_hex(curr_head));
-   update_ref(am, ORIG_HEAD, curr_head, NULL, 0, 
UPDATE_REFS_DIE_ON_ERR);
+   if (!state-rebasing)
+   update_ref(am, ORIG_HEAD, curr_head, NULL, 0,
+   UPDATE_REFS_DIE_ON_ERR);
} else {
write_file(am_path(state, abort-safety), 1, %s, );
-   delete_ref(ORIG_HEAD, NULL, 0);
+   if (!state-rebasing)
+   delete_ref(ORIG_HEAD, NULL, 0);
}
 
/*
@@ -1054,8 +1068,14 @@ next:
am_next(state);
}
 
-   am_destroy(state);
-   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+   /*
+* In rebasing mode, it's up to the caller to take care of
+* housekeeping.
+*/
+   if (!state-rebasing) {
+   am_destroy(state);
+   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+   }
 }
 
 /**
@@ -1325,6 +1345,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT_CMDMODE(0, abort, resume,
N_(restore the original branch and abort the patching 
operation.),
RESUME_ABORT),
+   OPT_HIDDEN_BOOL(0, rebasing, state.rebasing,
+   N_((internal use for git-rebase))),
OPT_END()
};
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 32/45] builtin-am: implement -S/--gpg-sign, commit.gpgsign

2015-08-04 Thread Paul Tan
Since 3b4e395 (am: add the --gpg-sign option, 2014-02-01), git-am.sh
supported the --gpg-sign option, and would pass it to git-commit-tree,
thus GPG-signing the commit object.

Re-implement this option in builtin/am.c.

git-commit-tree would also sign the commit by default if the
commit.gpgsign setting is true. Since we do not run commit-tree, we
re-implement this behavior by handling the commit.gpgsign setting
ourselves.

Helped-by: Stefan Beller sbel...@google.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 11 ++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 1561580..18611fa 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -110,6 +110,7 @@ struct am_state {
const char *resolvemsg;
int committer_date_is_author_date;
int ignore_date;
+   const char *sign_commit;
int rebasing;
 };
 
@@ -119,6 +120,8 @@ struct am_state {
  */
 static void am_state_init(struct am_state *state, const char *dir)
 {
+   int gpgsign;
+
memset(state, 0, sizeof(*state));
 
assert(dir);
@@ -133,6 +136,9 @@ static void am_state_init(struct am_state *state, const 
char *dir)
state-scissors = SCISSORS_UNSET;
 
argv_array_init(state-git_apply_opts);
+
+   if (!git_config_get_bool(commit.gpgsign, gpgsign))
+   state-sign_commit = gpgsign ?  : NULL;
 }
 
 /**
@@ -1227,7 +1233,7 @@ static void do_commit(const struct am_state *state)
state-ignore_date ?  : state-author_date, 1);
 
if (commit_tree(state-msg, state-msg_len, tree, parents, commit,
-   author, NULL))
+   author, state-sign_commit))
die(_(failed to write commit object));
 
reflog_msg = getenv(GIT_REFLOG_ACTION);
@@ -1673,6 +1679,9 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
N_(lie about committer date)),
OPT_BOOL(0, ignore-date, state.ignore_date,
N_(use current timestamp for author date)),
+   { OPTION_STRING, 'S', gpg-sign, state.sign_commit, 
N_(key-id),
+ N_(GPG-sign commits),
+ PARSE_OPT_OPTARG, NULL, (intptr_t)  },
OPT_HIDDEN_BOOL(0, rebasing, state.rebasing,
N_((internal use for git-rebase))),
OPT_END()
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 27/45] builtin-am: support --keep-cr, am.keepcr

2015-08-04 Thread Paul Tan
Since ad2c928 (git-am: Add command line parameter `--keep-cr` passing it
to git-mailsplit, 2010-02-27), git-am.sh supported the --keep-cr option
and would pass it to git-mailsplit.

Since e80d4cb (git-am: Add am.keepcr and --no-keep-cr to override it,
2010-02-27), git-am.sh supported the am.keepcr config setting, which
controls whether --keep-cr is on by default.

Re-implement the above in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 29 +++--
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 8e97839..e34bc51 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -502,7 +502,7 @@ done:
  * Splits out individual email patches from `paths`, where each path is either
  * a mbox file or a Maildir. Returns 0 on success, -1 on failure.
  */
-static int split_mail_mbox(struct am_state *state, const char **paths)
+static int split_mail_mbox(struct am_state *state, const char **paths, int 
keep_cr)
 {
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf last = STRBUF_INIT;
@@ -512,6 +512,8 @@ static int split_mail_mbox(struct am_state *state, const 
char **paths)
argv_array_pushf(cp.args, -d%d, state-prec);
argv_array_pushf(cp.args, -o%s, state-dir);
argv_array_push(cp.args, -b);
+   if (keep_cr)
+   argv_array_push(cp.args, --keep-cr);
argv_array_push(cp.args, --);
argv_array_pushv(cp.args, paths);
 
@@ -536,14 +538,22 @@ static int split_mail_mbox(struct am_state *state, const 
char **paths)
  * state-cur will be set to the index of the first mail, and state-last will
  * be set to the index of the last mail.
  *
+ * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1
+ * to disable this behavior, -1 to use the default configured setting.
+ *
  * Returns 0 on success, -1 on failure.
  */
 static int split_mail(struct am_state *state, enum patch_format patch_format,
-   const char **paths)
+   const char **paths, int keep_cr)
 {
+   if (keep_cr  0) {
+   keep_cr = 0;
+   git_config_get_bool(am.keepcr, keep_cr);
+   }
+
switch (patch_format) {
case PATCH_FORMAT_MBOX:
-   return split_mail_mbox(state, paths);
+   return split_mail_mbox(state, paths, keep_cr);
default:
die(BUG: invalid patch_format);
}
@@ -554,7 +564,7 @@ static int split_mail(struct am_state *state, enum 
patch_format patch_format,
  * Setup a new am session for applying patches
  */
 static void am_setup(struct am_state *state, enum patch_format patch_format,
-   const char **paths)
+   const char **paths, int keep_cr)
 {
unsigned char curr_head[GIT_SHA1_RAWSZ];
const char *str;
@@ -570,7 +580,7 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
if (mkdir(state-dir, 0777)  0  errno != EEXIST)
die_errno(_(failed to create directory '%s'), state-dir);
 
-   if (split_mail(state, patch_format, paths)  0) {
+   if (split_mail(state, patch_format, paths, keep_cr)  0) {
am_destroy(state);
die(_(Failed to split patches.));
}
@@ -1511,6 +1521,7 @@ enum resume_mode {
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
struct am_state state;
+   int keep_cr = -1;
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
 
@@ -1534,6 +1545,12 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
N_(pass -b flag to git-mailinfo), KEEP_NON_PATCH),
OPT_BOOL('m', message-id, state.message_id,
N_(pass -m flag to git-mailinfo)),
+   { OPTION_SET_INT, 0, keep-cr, keep_cr, NULL,
+ N_(pass --keep-cr flag to git-mailsplit for mbox format),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1},
+   { OPTION_SET_INT, 0, no-keep-cr, keep_cr, NULL,
+ N_(do not pass --keep-cr flag to git-mailsplit independent 
of am.keepcr),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
@@ -1631,7 +1648,7 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
argv_array_push(paths, mkpath(%s/%s, prefix, 
argv[i]));
}
 
-   am_setup(state, patch_format, paths.argv);
+   am_setup(state, patch_format, paths.argv, keep_cr);
 
argv_array_clear(paths);
}
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info

[PATCH v7 07/45] builtin-am: extract patch and commit info with git-mailinfo

2015-08-04 Thread Paul Tan
For the purpose of applying the patch and committing the results,
implement extracting the patch data, commit message and authorship from
an e-mail message using git-mailinfo.

git-mailinfo is run as a separate process, but ideally in the future,
we should be be able to access its functionality directly without
spawning a new process.

Helped-by: Junio C Hamano gits...@pobox.com
Helped-by: Jeff King p...@peff.net
Helped-by: Johannes Schindelin johannes.schinde...@gmx.de
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 317 +++
 1 file changed, 317 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index c12566a..63f0fa4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -9,6 +9,23 @@
 #include parse-options.h
 #include dir.h
 #include run-command.h
+#include quote.h
+
+/**
+ * Returns 1 if the file is empty or does not exist, 0 otherwise.
+ */
+static int is_empty_file(const char *filename)
+{
+   struct stat st;
+
+   if (stat(filename, st)  0) {
+   if (errno == ENOENT)
+   return 1;
+   die_errno(_(could not stat %s), filename);
+   }
+
+   return !st.st_size;
+}
 
 /**
  * Like strbuf_getline(), but treats both '\n' and \r\n as line terminators.
@@ -38,6 +55,13 @@ struct am_state {
int cur;
int last;
 
+   /* commit metadata and message */
+   char *author_name;
+   char *author_email;
+   char *author_date;
+   char *msg;
+   size_t msg_len;
+
/* number of digits in patch filename */
int prec;
 };
@@ -62,6 +86,10 @@ static void am_state_init(struct am_state *state, const char 
*dir)
 static void am_state_release(struct am_state *state)
 {
free(state-dir);
+   free(state-author_name);
+   free(state-author_email);
+   free(state-author_date);
+   free(state-msg);
 }
 
 /**
@@ -112,6 +140,161 @@ static int read_state_file(struct strbuf *sb, const 
struct am_state *state,
 }
 
 /**
+ * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE
+ * as a newly-allocated string. VALUE must be a quoted string, and the KEY must
+ * match `key`. Returns NULL on failure.
+ *
+ * This is used by read_author_script() to read the GIT_AUTHOR_* variables from
+ * the author-script.
+ */
+static char *read_shell_var(FILE *fp, const char *key)
+{
+   struct strbuf sb = STRBUF_INIT;
+   const char *str;
+
+   if (strbuf_getline(sb, fp, '\n'))
+   goto fail;
+
+   if (!skip_prefix(sb.buf, key, str))
+   goto fail;
+
+   if (!skip_prefix(str, =, str))
+   goto fail;
+
+   strbuf_remove(sb, 0, str - sb.buf);
+
+   str = sq_dequote(sb.buf);
+   if (!str)
+   goto fail;
+
+   return strbuf_detach(sb, NULL);
+
+fail:
+   strbuf_release(sb);
+   return NULL;
+}
+
+/**
+ * Reads and parses the state directory's author-script file, and sets
+ * state-author_name, state-author_email and state-author_date accordingly.
+ * Returns 0 on success, -1 if the file could not be parsed.
+ *
+ * The author script is of the format:
+ *
+ * GIT_AUTHOR_NAME='$author_name'
+ * GIT_AUTHOR_EMAIL='$author_email'
+ * GIT_AUTHOR_DATE='$author_date'
+ *
+ * where $author_name, $author_email and $author_date are quoted. We are strict
+ * with our parsing, as the file was meant to be eval'd in the old git-am.sh
+ * script, and thus if the file differs from what this function expects, it is
+ * better to bail out than to do something that the user does not expect.
+ */
+static int read_author_script(struct am_state *state)
+{
+   const char *filename = am_path(state, author-script);
+   FILE *fp;
+
+   assert(!state-author_name);
+   assert(!state-author_email);
+   assert(!state-author_date);
+
+   fp = fopen(filename, r);
+   if (!fp) {
+   if (errno == ENOENT)
+   return 0;
+   die_errno(_(could not open '%s' for reading), filename);
+   }
+
+   state-author_name = read_shell_var(fp, GIT_AUTHOR_NAME);
+   if (!state-author_name) {
+   fclose(fp);
+   return -1;
+   }
+
+   state-author_email = read_shell_var(fp, GIT_AUTHOR_EMAIL);
+   if (!state-author_email) {
+   fclose(fp);
+   return -1;
+   }
+
+   state-author_date = read_shell_var(fp, GIT_AUTHOR_DATE);
+   if (!state-author_date) {
+   fclose(fp);
+   return -1;
+   }
+
+   if (fgetc(fp) != EOF) {
+   fclose(fp);
+   return -1;
+   }
+
+   fclose(fp);
+   return 0;
+}
+
+/**
+ * Saves state-author_name, state-author_email and state-author_date in the
+ * state directory's author-script file.
+ */
+static void write_author_script(const struct am_state *state)
+{
+   struct strbuf sb = STRBUF_INIT;
+
+   strbuf_addstr(sb, GIT_AUTHOR_NAME

[PATCH v7 28/45] builtin-am: implement --[no-]scissors

2015-08-04 Thread Paul Tan
Since 017678b (am/mailinfo: Disable scissors processing by default,
2009-08-26), git-am supported the --[no-]scissors option, passing it to
git-mailinfo.

Re-implement support for this option in builtin/am.c.

Since the default setting of --scissors in git-mailinfo can be
configured with mailinfo.scissors (and perhaps through other settings in
the future), to be safe we make an explicit distinction between
SCISSORS_UNSET, SCISSORS_TRUE and SCISSORS_FALSE.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 48 
 1 file changed, 48 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index e34bc51..727cfb8 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -74,6 +74,12 @@ enum keep_type {
KEEP_NON_PATCH  /* pass -b flag to git-mailinfo */
 };
 
+enum scissors_type {
+   SCISSORS_UNSET = -1,
+   SCISSORS_FALSE = 0,  /* pass --no-scissors to git-mailinfo */
+   SCISSORS_TRUE/* pass --scissors to git-mailinfo */
+};
+
 struct am_state {
/* state directory path */
char *dir;
@@ -99,6 +105,7 @@ struct am_state {
int utf8;
int keep; /* enum keep_type */
int message_id;
+   int scissors; /* enum scissors_type */
const char *resolvemsg;
int rebasing;
 };
@@ -119,6 +126,8 @@ static void am_state_init(struct am_state *state, const 
char *dir)
state-utf8 = 1;
 
git_config_get_bool(am.messageid, state-message_id);
+
+   state-scissors = SCISSORS_UNSET;
 }
 
 /**
@@ -394,6 +403,14 @@ static void am_load(struct am_state *state)
read_state_file(sb, state, messageid, 1);
state-message_id = !strcmp(sb.buf, t);
 
+   read_state_file(sb, state, scissors, 1);
+   if (!strcmp(sb.buf, t))
+   state-scissors = SCISSORS_TRUE;
+   else if (!strcmp(sb.buf, f))
+   state-scissors = SCISSORS_FALSE;
+   else
+   state-scissors = SCISSORS_UNSET;
+
state-rebasing = !!file_exists(am_path(state, rebasing));
 
strbuf_release(sb);
@@ -614,6 +631,22 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 
write_file(am_path(state, messageid), 1, state-message_id ? t : 
f);
 
+   switch (state-scissors) {
+   case SCISSORS_UNSET:
+   str = ;
+   break;
+   case SCISSORS_FALSE:
+   str = f;
+   break;
+   case SCISSORS_TRUE:
+   str = t;
+   break;
+   default:
+   die(BUG: invalid value for state-scissors);
+   }
+
+   write_file(am_path(state, scissors), 1, %s, str);
+
if (state-rebasing)
write_file(am_path(state, rebasing), 1, %s, );
else
@@ -798,6 +831,19 @@ static int parse_mail(struct am_state *state, const char 
*mail)
if (state-message_id)
argv_array_push(cp.args, -m);
 
+   switch (state-scissors) {
+   case SCISSORS_UNSET:
+   break;
+   case SCISSORS_FALSE:
+   argv_array_push(cp.args, --no-scissors);
+   break;
+   case SCISSORS_TRUE:
+   argv_array_push(cp.args, --scissors);
+   break;
+   default:
+   die(BUG: invalid value for state-scissors);
+   }
+
argv_array_push(cp.args, am_path(state, msg));
argv_array_push(cp.args, am_path(state, patch));
 
@@ -1551,6 +1597,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
{ OPTION_SET_INT, 0, no-keep-cr, keep_cr, NULL,
  N_(do not pass --keep-cr flag to git-mailsplit independent 
of am.keepcr),
  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
+   OPT_BOOL('c', scissors, state.scissors,
+   N_(strip everything before a scissors line)),
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 38/45] builtin-am: rerere support

2015-08-04 Thread Paul Tan
git-am.sh will call git-rerere at the following events:

* git rerere when a three-way merge fails to record the conflicted
  automerge results. Since 8389b52 (git-rerere: reuse recorded resolve.,
  2006-01-28)

  * Since cb6020b (Teach --[no-]rerere-autoupdate option to merge,
revert and friends, 2009-12-04), git-am.sh supports the
--[no-]rerere-autoupdate option as well, and would pass it to
git-rerere.

* git rerere when --resolved, to record the hand resolution. Since
  f131dd4 (rerere: record (or avoid misrecording) resolved, skipped or
  aborted rebase/am, 2006-12-08)

* git rerere clear when --skip-ing. Since f131dd4 (rerere: record (or
  avoid misrecording) resolved, skipped or aborted rebase/am,
  2006-12-08)

* git rerere clear when --abort-ing. Since 3e5057a (git am --abort,
  2008-07-16)

Re-implement the above in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 25 +
 1 file changed, 25 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index c313e58..33d1f24 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -24,6 +24,7 @@
 #include revision.h
 #include log-tree.h
 #include notes-utils.h
+#include rerere.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -114,6 +115,7 @@ struct am_state {
const char *resolvemsg;
int committer_date_is_author_date;
int ignore_date;
+   int allow_rerere_autoupdate;
const char *sign_commit;
int rebasing;
 };
@@ -1312,6 +1314,7 @@ static int fall_back_threeway(const struct am_state 
*state, const char *index_pa
o.verbosity = 0;
 
if (merge_recursive_generic(o, our_tree, his_tree, 1, bases, result)) 
{
+   rerere(state-allow_rerere_autoupdate);
free(his_tree_name);
return error(_(Failed to merge in the changes.));
}
@@ -1531,6 +1534,8 @@ static void am_resolve(struct am_state *state)
die_user_resolve(state);
}
 
+   rerere(0);
+
do_commit(state);
 
am_next(state);
@@ -1631,12 +1636,29 @@ static int clean_index(const unsigned char *head, const 
unsigned char *remote)
 }
 
 /**
+ * Resets rerere's merge resolution metadata.
+ */
+static void am_rerere_clear(void)
+{
+   struct string_list merge_rr = STRING_LIST_INIT_DUP;
+   int fd = setup_rerere(merge_rr, 0);
+
+   if (fd  0)
+   return;
+
+   rerere_clear(merge_rr);
+   string_list_clear(merge_rr, 1);
+}
+
+/**
  * Resume the current am session by skipping the current patch.
  */
 static void am_skip(struct am_state *state)
 {
unsigned char head[GIT_SHA1_RAWSZ];
 
+   am_rerere_clear();
+
if (get_sha1(HEAD, head))
hashcpy(head, EMPTY_TREE_SHA1_BIN);
 
@@ -1694,6 +1716,8 @@ static void am_abort(struct am_state *state)
return;
}
 
+   am_rerere_clear();
+
curr_branch = resolve_refdup(HEAD, 0, curr_head, NULL);
has_curr_head = !is_null_sha1(curr_head);
if (!has_curr_head)
@@ -1823,6 +1847,7 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
N_(lie about committer date)),
OPT_BOOL(0, ignore-date, state.ignore_date,
N_(use current timestamp for author date)),
+   OPT_RERERE_AUTOUPDATE(state.allow_rerere_autoupdate),
{ OPTION_STRING, 'S', gpg-sign, state.sign_commit, 
N_(key-id),
  N_(GPG-sign commits),
  PARSE_OPT_OPTARG, NULL, (intptr_t)  },
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 12/45] builtin-am: don't parse mail when resuming

2015-08-04 Thread Paul Tan
Since 271440e (git-am: make it easier after fixing up an unapplicable
patch., 2005-10-25), when git am is run again after being paused, the
current mail message will not be re-parsed, but instead the contents of
the state directory's patch, msg and author-script files will be used
as-is instead.

Re-implement this in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 34 +-
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index fd26721..ec579a6 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -776,8 +776,12 @@ static void validate_resume_state(const struct am_state 
*state)
 
 /**
  * Applies all queued mail.
+ *
+ * If `resume` is true, we are resuming. The msg and authorship fields, as
+ * well as the state directory's patch file is used as-is for applying the
+ * patch and committing it.
  */
-static void am_run(struct am_state *state)
+static void am_run(struct am_state *state, int resume)
 {
const char *argv_gc_auto[] = {gc, --auto, NULL};
struct strbuf sb = STRBUF_INIT;
@@ -795,11 +799,16 @@ static void am_run(struct am_state *state)
if (!file_exists(mail))
goto next;
 
-   if (parse_mail(state, mail))
-   goto next; /* mail should be skipped */
+   if (resume) {
+   validate_resume_state(state);
+   resume = 0;
+   } else {
+   if (parse_mail(state, mail))
+   goto next; /* mail should be skipped */
 
-   write_author_script(state);
-   write_commit_msg(state);
+   write_author_script(state);
+   write_commit_msg(state);
+   }
 
printf_ln(_(Applying: %.*s), linelen(state-msg), state-msg);
 
@@ -855,7 +864,7 @@ static void am_resolve(struct am_state *state)
do_commit(state);
 
am_next(state);
-   am_run(state);
+   am_run(state, 0);
 }
 
 /**
@@ -875,6 +884,7 @@ static int parse_opt_patchformat(const struct option *opt, 
const char *arg, int
 
 enum resume_mode {
RESUME_FALSE = 0,
+   RESUME_APPLY,
RESUME_RESOLVED
 };
 
@@ -927,9 +937,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
if (read_index_preload(the_index, NULL)  0)
die(_(failed to read the index));
 
-   if (am_in_progress(state))
+   if (am_in_progress(state)) {
+   if (resume == RESUME_FALSE)
+   resume = RESUME_APPLY;
+
am_load(state);
-   else {
+   } else {
struct argv_array paths = ARGV_ARRAY_INIT;
int i;
 
@@ -950,7 +963,10 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
switch (resume) {
case RESUME_FALSE:
-   am_run(state);
+   am_run(state, 0);
+   break;
+   case RESUME_APPLY:
+   am_run(state, 1);
break;
case RESUME_RESOLVED:
am_resolve(state);
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 17/45] builtin-am: exit with user friendly message on failure

2015-08-04 Thread Paul Tan
Since ced9456 (Give the user a hint for how to continue in the case that
git-am fails because it requires user intervention, 2006-05-02), git-am
prints additional information on how the user can re-invoke git-am to
resume patch application after resolving the failure. Re-implement this
through the die_user_resolve() function.

Since cc12005 (Make git rebase interactive help match documentation.,
2006-05-13), git-am supports the --resolvemsg option which is used by
git-rebase to override the message printed out when git-am fails.
Re-implement this option.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 30 ++
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 0875e69..8b8f2da 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -83,6 +83,7 @@ struct am_state {
 
/* various operating modes and command line options */
int quiet;
+   const char *resolvemsg;
 };
 
 /**
@@ -647,6 +648,25 @@ static int index_has_changes(struct strbuf *sb)
 }
 
 /**
+ * Dies with a user-friendly message on how to proceed after resolving the
+ * problem. This message can be overridden with state-resolvemsg.
+ */
+static void NORETURN die_user_resolve(const struct am_state *state)
+{
+   if (state-resolvemsg) {
+   printf_ln(%s, state-resolvemsg);
+   } else {
+   const char *cmdline = git am;
+
+   printf_ln(_(When you have resolved this problem, run \%s 
--continue\.), cmdline);
+   printf_ln(_(If you prefer to skip this patch, run \%s 
--skip\ instead.), cmdline);
+   printf_ln(_(To restore the original branch and stop patching, 
run \%s --abort\.), cmdline);
+   }
+
+   exit(128);
+}
+
+/**
  * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
  * state-msg will be set to the patch message. state-author_name,
  * state-author_email and state-author_date will be set to the patch author's
@@ -706,7 +726,7 @@ static int parse_mail(struct am_state *state, const char 
*mail)
 
if (is_empty_file(am_path(state, patch))) {
printf_ln(_(Patch is empty. Was it split wrong?));
-   exit(128);
+   die_user_resolve(state);
}
 
strbuf_addstr(msg, \n\n);
@@ -871,7 +891,7 @@ static void am_run(struct am_state *state, int resume)
printf_ln(_(The copy of the patch that failed 
is found in: %s),
am_path(state, patch));
 
-   exit(128);
+   die_user_resolve(state);
}
 
do_commit(state);
@@ -899,13 +919,13 @@ static void am_resolve(struct am_state *state)
printf_ln(_(No changes - did you forget to use 'git add'?\n
If there is nothing left to stage, chances are that 
something else\n
already introduced the same changes; you might want to 
skip this patch.));
-   exit(128);
+   die_user_resolve(state);
}
 
if (unmerged_cache()) {
printf_ln(_(You still have unmerged paths in your index.\n
Did you forget to use 'git add'?));
-   exit(128);
+   die_user_resolve(state);
}
 
do_commit(state);
@@ -1133,6 +1153,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
+   OPT_STRING(0, resolvemsg, state.resolvemsg, NULL,
+   N_(override error message when patch failure occurs)),
OPT_CMDMODE(0, continue, resume,
N_(continue applying patches after resolving a 
conflict),
RESUME_RESOLVED),
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 26/45] builtin-am: implement --[no-]message-id, am.messageid

2015-08-04 Thread Paul Tan
Since a078f73 (git-am: add --message-id/--no-message-id, 2014-11-25),
git-am.sh supported the --[no-]message-id options, and the
am.messageid setting which specifies the default option.

--[no-]message-id tells git-am whether or not the -m option should be
passed to git-mailinfo.

Re-implement this option in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 68dca2e..8e97839 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -98,6 +98,7 @@ struct am_state {
int signoff;
int utf8;
int keep; /* enum keep_type */
+   int message_id;
const char *resolvemsg;
int rebasing;
 };
@@ -116,6 +117,8 @@ static void am_state_init(struct am_state *state, const 
char *dir)
state-prec = 4;
 
state-utf8 = 1;
+
+   git_config_get_bool(am.messageid, state-message_id);
 }
 
 /**
@@ -388,6 +391,9 @@ static void am_load(struct am_state *state)
else
state-keep = KEEP_FALSE;
 
+   read_state_file(sb, state, messageid, 1);
+   state-message_id = !strcmp(sb.buf, t);
+
state-rebasing = !!file_exists(am_path(state, rebasing));
 
strbuf_release(sb);
@@ -596,6 +602,8 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 
write_file(am_path(state, keep), 1, %s, str);
 
+   write_file(am_path(state, messageid), 1, state-message_id ? t : 
f);
+
if (state-rebasing)
write_file(am_path(state, rebasing), 1, %s, );
else
@@ -777,6 +785,9 @@ static int parse_mail(struct am_state *state, const char 
*mail)
die(BUG: invalid value for state-keep);
}
 
+   if (state-message_id)
+   argv_array_push(cp.args, -m);
+
argv_array_push(cp.args, am_path(state, msg));
argv_array_push(cp.args, am_path(state, patch));
 
@@ -1521,6 +1532,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
N_(pass -k flag to git-mailinfo), KEEP_TRUE),
OPT_SET_INT(0, keep-non-patch, state.keep,
N_(pass -b flag to git-mailinfo), KEEP_NON_PATCH),
+   OPT_BOOL('m', message-id, state.message_id,
+   N_(pass -m flag to git-mailinfo)),
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 31/45] builtin-am: implement --committer-date-is-author-date

2015-08-04 Thread Paul Tan
Since 3f01ad6 (am: Add --committer-date-is-author-date option,
2009-01-22), git-am.sh implemented the --committer-date-is-author-date
option, which tells git-am to use the timestamp recorded in the email
message as both author and committer date.

Re-implement this option in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 84d3e05..1561580 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -108,6 +108,7 @@ struct am_state {
int scissors; /* enum scissors_type */
struct argv_array git_apply_opts;
const char *resolvemsg;
+   int committer_date_is_author_date;
int ignore_date;
int rebasing;
 };
@@ -1221,6 +1222,10 @@ static void do_commit(const struct am_state *state)
state-ignore_date ? NULL : state-author_date,
IDENT_STRICT);
 
+   if (state-committer_date_is_author_date)
+   setenv(GIT_COMMITTER_DATE,
+   state-ignore_date ?  : state-author_date, 1);
+
if (commit_tree(state-msg, state-msg_len, tree, parents, commit,
author, NULL))
die(_(failed to write commit object));
@@ -1663,6 +1668,9 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT_CMDMODE(0, abort, resume,
N_(restore the original branch and abort the patching 
operation.),
RESUME_ABORT),
+   OPT_BOOL(0, committer-date-is-author-date,
+   state.committer_date_is_author_date,
+   N_(lie about committer date)),
OPT_BOOL(0, ignore-date, state.ignore_date,
N_(use current timestamp for author date)),
OPT_HIDDEN_BOOL(0, rebasing, state.rebasing,
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 18/45] builtin-am: implement -s/--signoff

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), git-am
supported the --signoff option which will append a signoff at the end of
the commit messsage. Re-implement this feature in parse_mail() by
calling append_signoff() if the option is set.

Signed-off-by: Paul Tan pyoka...@gmail.com
---

Notes:
v7

* Having a field named append_signoff takes up a lot of horizontal
  space when referring to it. Shorten the name to just signoff.

 builtin/am.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 8b8f2da..12952cf 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -18,6 +18,7 @@
 #include diffcore.h
 #include unpack-trees.h
 #include branch.h
+#include sequencer.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -83,6 +84,7 @@ struct am_state {
 
/* various operating modes and command line options */
int quiet;
+   int signoff;
const char *resolvemsg;
 };
 
@@ -353,6 +355,9 @@ static void am_load(struct am_state *state)
read_state_file(sb, state, quiet, 1);
state-quiet = !strcmp(sb.buf, t);
 
+   read_state_file(sb, state, sign, 1);
+   state-signoff = !strcmp(sb.buf, t);
+
strbuf_release(sb);
 }
 
@@ -533,6 +538,8 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 
write_file(am_path(state, quiet), 1, state-quiet ? t : f);
 
+   write_file(am_path(state, sign), 1, state-signoff ? t : f);
+
if (!get_sha1(HEAD, curr_head)) {
write_file(am_path(state, abort-safety), 1, %s, 
sha1_to_hex(curr_head));
update_ref(am, ORIG_HEAD, curr_head, NULL, 0, 
UPDATE_REFS_DIE_ON_ERR);
@@ -734,6 +741,9 @@ static int parse_mail(struct am_state *state, const char 
*mail)
die_errno(_(could not read '%s'), am_path(state, msg));
stripspace(msg, 0);
 
+   if (state-signoff)
+   append_signoff(msg, 0, 0);
+
assert(!state-author_name);
state-author_name = strbuf_detach(author_name, NULL);
 
@@ -1150,6 +1160,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
 
struct option options[] = {
OPT__QUIET(state.quiet, N_(be quiet)),
+   OPT_BOOL('s', signoff, state.signoff,
+   N_(add a Signed-off-by line to the commit message)),
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 15/45] builtin-am: reject patches when there's a session in progress

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07), git-am
would error out if the user gave it mbox(s) on the command-line, but
there was a session in progress.

Since c95b138 (Fix git-am safety checks, 2006-09-15), git-am would
detect if the user attempted to feed it a mbox via stdin, by checking if
stdin is not a tty and there is no resume command given.

Re-implement the above two safety checks.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 6c24d07..d4b4b86 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1148,6 +1148,21 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
die(_(failed to read the index));
 
if (am_in_progress(state)) {
+   /*
+* Catch user error to feed us patches when there is a session
+* in progress:
+*
+* 1. mbox path(s) are provided on the command-line.
+* 2. stdin is not a tty: the user is trying to feed us a patch
+*from standard input. This is somewhat unreliable -- stdin
+*could be /dev/null for example and the caller did not
+*intend to feed us a patch but wanted to continue
+*unattended.
+*/
+   if (argc || (resume == RESUME_FALSE  !isatty(0)))
+   die(_(previous rebase directory %s still exists but 
mbox given.),
+   state.dir);
+
if (resume == RESUME_FALSE)
resume = RESUME_APPLY;
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 35/45] builtin-am: invoke applypatch-msg hook

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sh will invoke the applypatch-msg hooks just after extracting the
patch message. If the applypatch-msg hook exits with a non-zero status,
git-am.sh abort before even applying the patch to the index.

Re-implement this in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 7d7f91d..f0e3aab 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -456,6 +456,27 @@ static void am_destroy(const struct am_state *state)
 }
 
 /**
+ * Runs applypatch-msg hook. Returns its exit code.
+ */
+static int run_applypatch_msg_hook(struct am_state *state)
+{
+   int ret;
+
+   assert(state-msg);
+   ret = run_hook_le(NULL, applypatch-msg, am_path(state, 
final-commit), NULL);
+
+   if (!ret) {
+   free(state-msg);
+   state-msg = NULL;
+   if (read_commit_msg(state)  0)
+   die(_('%s' was deleted by the applypatch-msg hook),
+   am_path(state, final-commit));
+   }
+
+   return ret;
+}
+
+/**
  * Runs post-rewrite hook. Returns it exit code.
  */
 static int run_post_rewrite_hook(const struct am_state *state)
@@ -1420,6 +1441,9 @@ static void am_run(struct am_state *state, int resume)
write_commit_msg(state);
}
 
+   if (run_applypatch_msg_hook(state))
+   exit(1);
+
say(state, stdout, _(Applying: %.*s), linelen(state-msg), 
state-msg);
 
apply_status = run_apply(state, NULL);
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 24/45] builtin-am: implement -u/--utf8

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sh supported the -u,--utf8 option. If set, the -u option will be
passed to git-mailinfo to re-code the commit log message and authorship
in the charset specified by i18n.commitencoding. If unset, the -n option
will be passed to git-mailinfo, which disables the re-encoding.

Since d84029b (--utf8 is now default for 'git-am', 2007-01-08), --utf8
is specified by default in git-am.sh.

Re-implement the above in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 47dd4c7..528b2c9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -90,6 +90,7 @@ struct am_state {
int threeway;
int quiet;
int signoff;
+   int utf8;
const char *resolvemsg;
int rebasing;
 };
@@ -106,6 +107,8 @@ static void am_state_init(struct am_state *state, const 
char *dir)
state-dir = xstrdup(dir);
 
state-prec = 4;
+
+   state-utf8 = 1;
 }
 
 /**
@@ -367,6 +370,9 @@ static void am_load(struct am_state *state)
read_state_file(sb, state, sign, 1);
state-signoff = !strcmp(sb.buf, t);
 
+   read_state_file(sb, state, utf8, 1);
+   state-utf8 = !strcmp(sb.buf, t);
+
state-rebasing = !!file_exists(am_path(state, rebasing));
 
strbuf_release(sb);
@@ -556,6 +562,8 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 
write_file(am_path(state, sign), 1, state-signoff ? t : f);
 
+   write_file(am_path(state, utf8), 1, state-utf8 ? t : f);
+
if (state-rebasing)
write_file(am_path(state, rebasing), 1, %s, );
else
@@ -722,6 +730,7 @@ static int parse_mail(struct am_state *state, const char 
*mail)
cp.out = xopen(am_path(state, info), O_WRONLY | O_CREAT, 0777);
 
argv_array_push(cp.args, mailinfo);
+   argv_array_push(cp.args, state-utf8 ? -u : -n);
argv_array_push(cp.args, am_path(state, msg));
argv_array_push(cp.args, am_path(state, patch));
 
@@ -1460,6 +1469,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT__QUIET(state.quiet, N_(be quiet)),
OPT_BOOL('s', signoff, state.signoff,
N_(add a Signed-off-by line to the commit message)),
+   OPT_BOOL('u', utf8, state.utf8,
+   N_(recode into utf8 (default))),
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 45/45] builtin-am: remove redirection to git-am.sh

2015-08-04 Thread Paul Tan
At the beginning of the rewrite of git-am.sh to C, in order to not break
existing test scripts that depended on a functional git-am, a
redirection to git-am.sh was introduced that would activate if the
environment variable _GIT_USE_BUILTIN_AM was not defined.

Now that all of git-am.sh's functionality has been re-implemented in
builtin/am.c, remove this redirection, and retire git-am.sh into
contrib/examples/.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 Makefile|  1 -
 builtin/am.c| 15 ---
 git-am.sh = contrib/examples/git-am.sh |  0
 git.c   |  7 +--
 4 files changed, 1 insertion(+), 22 deletions(-)
 rename git-am.sh = contrib/examples/git-am.sh (100%)

diff --git a/Makefile b/Makefile
index da451f8..e39ca6c 100644
--- a/Makefile
+++ b/Makefile
@@ -467,7 +467,6 @@ TEST_PROGRAMS_NEED_X =
 # interactive shell sessions without exporting it.
 unexport CDPATH
 
-SCRIPT_SH += git-am.sh
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
diff --git a/builtin/am.c b/builtin/am.c
index 1ff74ac..84d57d4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -2221,21 +2221,6 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT_END()
};
 
-   /*
-* NEEDSWORK: Once all the features of git-am.sh have been
-* re-implemented in builtin/am.c, this preamble can be removed.
-*/
-   if (!getenv(_GIT_USE_BUILTIN_AM)) {
-   const char *path = mkpath(%s/git-am, git_exec_path());
-
-   if (sane_execvp(path, (char **)argv)  0)
-   die_errno(could not exec %s, path);
-   } else {
-   prefix = setup_git_directory();
-   trace_repo_setup(prefix);
-   setup_work_tree();
-   }
-
git_config(git_default_config, NULL);
 
am_state_init(state, git_path(rebase-apply));
diff --git a/git-am.sh b/contrib/examples/git-am.sh
similarity index 100%
rename from git-am.sh
rename to contrib/examples/git-am.sh
diff --git a/git.c b/git.c
index 38d9ad5..5feba41 100644
--- a/git.c
+++ b/git.c
@@ -370,12 +370,7 @@ static int run_builtin(struct cmd_struct *p, int argc, 
const char **argv)
 
 static struct cmd_struct commands[] = {
{ add, cmd_add, RUN_SETUP | NEED_WORK_TREE },
-   /*
-* NEEDSWORK: Once the redirection to git-am.sh in builtin/am.c has
-* been removed, this entry should be changed to
-* RUN_SETUP | NEED_WORK_TREE
-*/
-   { am, cmd_am },
+   { am, cmd_am, RUN_SETUP | NEED_WORK_TREE },
{ annotate, cmd_annotate, RUN_SETUP },
{ apply, cmd_apply, RUN_SETUP_GENTLY },
{ archive, cmd_archive },
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 20/45] builtin-am: implement --3way

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sh supported the --3way option, and if set, would attempt to do a
3-way merge if the initial patch application fails.

Since 5d86861 (am -3: list the paths that needed 3-way fallback,
2012-03-28), in a 3-way merge git-am.sh would list the paths that needed
3-way fallback, so that the user can review them more carefully to spot
mismerges.

Re-implement the above in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---

Notes:
v7

* As support for am.threeWay was removed in master, this patch now
  does not implement am.threeWay as well.

 builtin/am.c | 154 +--
 1 file changed, 150 insertions(+), 4 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 12952cf..a5d5e8c 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -19,6 +19,8 @@
 #include unpack-trees.h
 #include branch.h
 #include sequencer.h
+#include revision.h
+#include merge-recursive.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -83,6 +85,7 @@ struct am_state {
int prec;
 
/* various operating modes and command line options */
+   int threeway;
int quiet;
int signoff;
const char *resolvemsg;
@@ -352,6 +355,9 @@ static void am_load(struct am_state *state)
 
read_commit_msg(state);
 
+   read_state_file(sb, state, threeway, 1);
+   state-threeway = !strcmp(sb.buf, t);
+
read_state_file(sb, state, quiet, 1);
state-quiet = !strcmp(sb.buf, t);
 
@@ -536,6 +542,8 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
die(_(Failed to split patches.));
}
 
+   write_file(am_path(state, threeway), 1, state-threeway ? t : f);
+
write_file(am_path(state, quiet), 1, state-quiet ? t : f);
 
write_file(am_path(state, sign), 1, state-signoff ? t : f);
@@ -766,16 +774,34 @@ finish:
 }
 
 /**
- * Applies current patch with git-apply. Returns 0 on success, -1 otherwise.
+ * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If
+ * `index_file` is not NULL, the patch will be applied to that index.
  */
-static int run_apply(const struct am_state *state)
+static int run_apply(const struct am_state *state, const char *index_file)
 {
struct child_process cp = CHILD_PROCESS_INIT;
 
cp.git_cmd = 1;
 
+   if (index_file)
+   argv_array_pushf(cp.env_array, GIT_INDEX_FILE=%s, 
index_file);
+
+   /*
+* If we are allowed to fall back on 3-way merge, don't give false
+* errors during the initial attempt.
+*/
+   if (state-threeway  !index_file) {
+   cp.no_stdout = 1;
+   cp.no_stderr = 1;
+   }
+
argv_array_push(cp.args, apply);
-   argv_array_push(cp.args, --index);
+
+   if (index_file)
+   argv_array_push(cp.args, --cached);
+   else
+   argv_array_push(cp.args, --index);
+
argv_array_push(cp.args, am_path(state, patch));
 
if (run_command(cp))
@@ -783,8 +809,106 @@ static int run_apply(const struct am_state *state)
 
/* Reload index as git-apply will have modified it. */
discard_cache();
+   read_cache_from(index_file ? index_file : get_index_file());
+
+   return 0;
+}
+
+/**
+ * Builds an index that contains just the blobs needed for a 3way merge.
+ */
+static int build_fake_ancestor(const struct am_state *state, const char 
*index_file)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+
+   cp.git_cmd = 1;
+   argv_array_push(cp.args, apply);
+   argv_array_pushf(cp.args, --build-fake-ancestor=%s, index_file);
+   argv_array_push(cp.args, am_path(state, patch));
+
+   if (run_command(cp))
+   return -1;
+
+   return 0;
+}
+
+/**
+ * Attempt a threeway merge, using index_path as the temporary index.
+ */
+static int fall_back_threeway(const struct am_state *state, const char 
*index_path)
+{
+   unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],
+ our_tree[GIT_SHA1_RAWSZ];
+   const unsigned char *bases[1] = {orig_tree};
+   struct merge_options o;
+   struct commit *result;
+   char *his_tree_name;
+
+   if (get_sha1(HEAD, our_tree)  0)
+   hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);
+
+   if (build_fake_ancestor(state, index_path))
+   return error(could not build fake ancestor);
+
+   discard_cache();
+   read_cache_from(index_path);
+
+   if (write_index_as_tree(orig_tree, the_index, index_path, 0, NULL))
+   return error(_(Repository lacks necessary blobs to fall back 
on 3-way merge.));
+
+   say(state, stdout, _(Using index info to reconstruct a base tree...));
+
+   if (!state-quiet) {
+   /*
+* List paths that needed 3-way fallback, so that the user can

[PATCH v7 40/45] builtin-am: support and auto-detect StGit series files

2015-08-04 Thread Paul Tan
Since c574e68 (git-am foreign patch support: StGIT support, 2009-05-27),
git-am.sh is able to read a single StGit series file and, for each StGit
patch listed in the file, convert the StGit patch into a RFC2822 mail
patch suitable for parsing with git-mailinfo, and queue them in the
state directory for applying.

Since 15ced75 (git-am foreign patch support: autodetect some patch
formats, 2009-05-27), git-am.sh is able to auto-detect StGit series
files by checking to see if the file starts with the string:

# This series applies on GIT commit

Re-implement the above in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 59 ++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index d82d07e..3c2ec15 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -80,7 +80,8 @@ static int str_isspace(const char *str)
 enum patch_format {
PATCH_FORMAT_UNKNOWN = 0,
PATCH_FORMAT_MBOX,
-   PATCH_FORMAT_STGIT
+   PATCH_FORMAT_STGIT,
+   PATCH_FORMAT_STGIT_SERIES
 };
 
 enum keep_type {
@@ -650,6 +651,11 @@ static int detect_patch_format(const char **paths)
goto done;
}
 
+   if (starts_with(l1.buf, # This series applies on GIT commit)) {
+   ret = PATCH_FORMAT_STGIT_SERIES;
+   goto done;
+   }
+
strbuf_reset(l2);
strbuf_getline_crlf(l2, fp);
strbuf_reset(l3);
@@ -801,6 +807,53 @@ static int stgit_patch_to_mail(FILE *out, FILE *in, int 
keep_cr)
 }
 
 /**
+ * This function only supports a single StGit series file in `paths`.
+ *
+ * Given an StGit series file, converts the StGit patches in the series into
+ * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in
+ * the state directory.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int split_mail_stgit_series(struct am_state *state, const char **paths,
+   int keep_cr)
+{
+   const char *series_dir;
+   char *series_dir_buf;
+   FILE *fp;
+   struct argv_array patches = ARGV_ARRAY_INIT;
+   struct strbuf sb = STRBUF_INIT;
+   int ret;
+
+   if (!paths[0] || paths[1])
+   return error(_(Only one StGIT patch series can be applied at 
once));
+
+   series_dir_buf = xstrdup(*paths);
+   series_dir = dirname(series_dir_buf);
+
+   fp = fopen(*paths, r);
+   if (!fp)
+   return error(_(could not open '%s' for reading: %s), *paths,
+   strerror(errno));
+
+   while (!strbuf_getline(sb, fp, '\n')) {
+   if (*sb.buf == '#')
+   continue; /* skip comment lines */
+
+   argv_array_push(patches, mkpath(%s/%s, series_dir, sb.buf));
+   }
+
+   fclose(fp);
+   strbuf_release(sb);
+   free(series_dir_buf);
+
+   ret = split_mail_conv(stgit_patch_to_mail, state, patches.argv, 
keep_cr);
+
+   argv_array_clear(patches);
+   return ret;
+}
+
+/**
  * Splits a list of files/directories into individual email patches. Each path
  * in `paths` must be a file/directory that is formatted according to
  * `patch_format`.
@@ -830,6 +883,8 @@ static int split_mail(struct am_state *state, enum 
patch_format patch_format,
return split_mail_mbox(state, paths, keep_cr);
case PATCH_FORMAT_STGIT:
return split_mail_conv(stgit_patch_to_mail, state, paths, 
keep_cr);
+   case PATCH_FORMAT_STGIT_SERIES:
+   return split_mail_stgit_series(state, paths, keep_cr);
default:
die(BUG: invalid patch_format);
}
@@ -1880,6 +1935,8 @@ static int parse_opt_patchformat(const struct option 
*opt, const char *arg, int
*opt_value = PATCH_FORMAT_MBOX;
else if (!strcmp(arg, stgit))
*opt_value = PATCH_FORMAT_STGIT;
+   else if (!strcmp(arg, stgit-series))
+   *opt_value = PATCH_FORMAT_STGIT_SERIES;
else
return error(_(Invalid value for --patch-format: %s), arg);
return 0;
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 43/45] builtin-am: implement legacy -b/--binary option

2015-08-04 Thread Paul Tan
The -b/--binary option was initially implemented in 087b674 (git-am:
--binary; document --resume and --binary., 2005-11-16). The option will
pass the --binary flag to git-apply to allow it to apply binary patches.

However, in 2b6eef9 (Make apply --binary a no-op., 2006-09-06), --binary
was been made a no-op in git-apply. Following that, since cb3a160
(git-am: ignore --binary option, 2008-08-09), the --binary option in
git-am is ignored as well.

In 6c15a1c (am: officially deprecate -b/--binary option, 2012-03-13),
the --binary option was tweaked to its present behavior: when set, the
message:

The -b/--binary option has been a no-op for long time, and it
will be removed. Please do not use it anymore.

will be printed.

Re-implement this in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 589199f..3c50392 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -2126,6 +2126,7 @@ enum resume_mode {
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
struct am_state state;
+   int binary = -1;
int keep_cr = -1;
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
@@ -2139,6 +2140,8 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
struct option options[] = {
OPT_BOOL('i', interactive, state.interactive,
N_(run interactively)),
+   OPT_HIDDEN_BOOL('b', binary, binary,
+   N_((historical option -- no-op)),
OPT_BOOL('3', 3way, state.threeway,
N_(allow fall back on 3way merging if needed)),
OPT__QUIET(state.quiet, N_(be quiet)),
@@ -2239,6 +2242,10 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
 
argc = parse_options(argc, argv, prefix, options, usage, 0);
 
+   if (binary = 0)
+   fprintf_ln(stderr, _(The -b/--binary option has been a no-op 
for long time, and\n
+   it will be removed. Please do not use it 
anymore.));
+
if (read_index_preload(the_index, NULL)  0)
die(_(failed to read the index));
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 11/45] builtin-am: implement --resolved/--continue

2015-08-04 Thread Paul Tan
Since 0c15cc9 (git-am: --resolved., 2005-11-16), git-am supported
resuming from a failed patch application. The user will manually apply
the patch, and the run git am --resolved which will then commit the
resulting index. Re-implement this feature by introducing am_resolve().

Since it makes no sense for the user to run am --resolved when there is
no session in progress, we error out in this case.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 72 +++-
 1 file changed, 71 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 537ad62..fd26721 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -760,6 +760,21 @@ static void do_commit(const struct am_state *state)
 }
 
 /**
+ * Validates the am_state for resuming -- the msg and authorship fields must
+ * be filled up.
+ */
+static void validate_resume_state(const struct am_state *state)
+{
+   if (!state-msg)
+   die(_(cannot resume: %s does not exist.),
+   am_path(state, final-commit));
+
+   if (!state-author_name || !state-author_email || !state-author_date)
+   die(_(cannot resume: %s does not exist.),
+   am_path(state, author-script));
+}
+
+/**
  * Applies all queued mail.
  */
 static void am_run(struct am_state *state)
@@ -814,6 +829,36 @@ next:
 }
 
 /**
+ * Resume the current am session after patch application failure. The user did
+ * all the hard work, and we do not have to do any patch application. Just
+ * trust and commit what the user has in the index and working tree.
+ */
+static void am_resolve(struct am_state *state)
+{
+   validate_resume_state(state);
+
+   printf_ln(_(Applying: %.*s), linelen(state-msg), state-msg);
+
+   if (!index_has_changes(NULL)) {
+   printf_ln(_(No changes - did you forget to use 'git add'?\n
+   If there is nothing left to stage, chances are that 
something else\n
+   already introduced the same changes; you might want to 
skip this patch.));
+   exit(128);
+   }
+
+   if (unmerged_cache()) {
+   printf_ln(_(You still have unmerged paths in your index.\n
+   Did you forget to use 'git add'?));
+   exit(128);
+   }
+
+   do_commit(state);
+
+   am_next(state);
+   am_run(state);
+}
+
+/**
  * parse_options() callback that validates and sets opt-value to the
  * PATCH_FORMAT_* enum value corresponding to `arg`.
  */
@@ -828,13 +873,20 @@ static int parse_opt_patchformat(const struct option 
*opt, const char *arg, int
return 0;
 }
 
+enum resume_mode {
+   RESUME_FALSE = 0,
+   RESUME_RESOLVED
+};
+
 int cmd_am(int argc, const char **argv, const char *prefix)
 {
struct am_state state;
int patch_format = PATCH_FORMAT_UNKNOWN;
+   enum resume_mode resume = RESUME_FALSE;
 
const char * const usage[] = {
N_(git am [options] [(mbox|Maildir)...]),
+   N_(git am [options] --continue),
NULL
};
 
@@ -842,6 +894,12 @@ int cmd_am(int argc, const char **argv, const char *prefix)
OPT_CALLBACK(0, patch-format, patch_format, N_(format),
N_(format the patch(es) are in),
parse_opt_patchformat),
+   OPT_CMDMODE(0, continue, resume,
+   N_(continue applying patches after resolving a 
conflict),
+   RESUME_RESOLVED),
+   OPT_CMDMODE('r', resolved, resume,
+   N_(synonyms for --continue),
+   RESUME_RESOLVED),
OPT_END()
};
 
@@ -875,6 +933,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
struct argv_array paths = ARGV_ARRAY_INIT;
int i;
 
+   if (resume)
+   die(_(Resolve operation not in progress, we are not 
resuming.));
+
for (i = 0; i  argc; i++) {
if (is_absolute_path(argv[i]) || !prefix)
argv_array_push(paths, argv[i]);
@@ -887,7 +948,16 @@ int cmd_am(int argc, const char **argv, const char *prefix)
argv_array_clear(paths);
}
 
-   am_run(state);
+   switch (resume) {
+   case RESUME_FALSE:
+   am_run(state);
+   break;
+   case RESUME_RESOLVED:
+   am_resolve(state);
+   break;
+   default:
+   die(BUG: invalid resume value);
+   }
 
am_state_release(state);
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 09/45] builtin-am: implement committing applied patch

2015-08-04 Thread Paul Tan
Implement do_commit(), which commits the index which contains the
results of applying the patch, along with the extracted commit message
and authorship information.

Since 29b6754 (am: remove rebase-apply directory before gc, 2010-02-22),
git gc --auto is also invoked to pack the loose objects that are created
from making the commits.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 55 +++
 1 file changed, 51 insertions(+), 4 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 1f198e4..a2811b6 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -11,6 +11,9 @@
 #include run-command.h
 #include quote.h
 #include lockfile.h
+#include cache-tree.h
+#include refs.h
+#include commit.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -674,10 +677,56 @@ static int run_apply(const struct am_state *state)
 }
 
 /**
+ * Commits the current index with state-msg as the commit message and
+ * state-author_name, state-author_email and state-author_date as the author
+ * information.
+ */
+static void do_commit(const struct am_state *state)
+{
+   unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],
+ commit[GIT_SHA1_RAWSZ];
+   unsigned char *ptr;
+   struct commit_list *parents = NULL;
+   const char *reflog_msg, *author;
+   struct strbuf sb = STRBUF_INIT;
+
+   if (write_cache_as_tree(tree, 0, NULL))
+   die(_(git write-tree failed to write a tree));
+
+   if (!get_sha1_commit(HEAD, parent)) {
+   ptr = parent;
+   commit_list_insert(lookup_commit(parent), parents);
+   } else {
+   ptr = NULL;
+   fprintf_ln(stderr, _(applying to an empty history));
+   }
+
+   author = fmt_ident(state-author_name, state-author_email,
+   state-author_date, IDENT_STRICT);
+
+   if (commit_tree(state-msg, state-msg_len, tree, parents, commit,
+   author, NULL))
+   die(_(failed to write commit object));
+
+   reflog_msg = getenv(GIT_REFLOG_ACTION);
+   if (!reflog_msg)
+   reflog_msg = am;
+
+   strbuf_addf(sb, %s: %.*s, reflog_msg, linelen(state-msg),
+   state-msg);
+
+   update_ref(sb.buf, HEAD, commit, ptr, 0, UPDATE_REFS_DIE_ON_ERR);
+
+   strbuf_release(sb);
+}
+
+/**
  * Applies all queued mail.
  */
 static void am_run(struct am_state *state)
 {
+   const char *argv_gc_auto[] = {gc, --auto, NULL};
+
refresh_and_write_cache();
 
while (state-cur = state-last) {
@@ -709,16 +758,14 @@ static void am_run(struct am_state *state)
exit(128);
}
 
-   /*
-* NEEDSWORK: After the patch has been applied to the index
-* with git-apply, we need to make commit as well.
-*/
+   do_commit(state);
 
 next:
am_next(state);
}
 
am_destroy(state);
+   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
 }
 
 /**
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 44/45] builtin-am: check for valid committer ident

2015-08-04 Thread Paul Tan
When commit_tree() is called, if the user does not have an explicit
committer ident configured, it will attempt to construct a default
committer ident based on the user's and system's info (e.g. gecos field,
hostname etc.) However, if a default committer ident is unable to be
constructed, commit_tree() will die(), but at this point of git-am's
execution, there will already be changes made to the index and work
tree.

This can be confusing to new users, and as such since d64e6b0 (Keep
Porcelainish from failing by broken ident after making changes.,
2006-02-18) git-am.sh will check to see if the committer ident has been
configured, or a default one can be constructed, before even starting to
apply patches.

Re-implement this in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 3c50392..1ff74ac 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -2246,6 +2246,9 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
fprintf_ln(stderr, _(The -b/--binary option has been a no-op 
for long time, and\n
it will be removed. Please do not use it 
anymore.));
 
+   /* Ensure a valid committer ident can be constructed */
+   git_committer_info(IDENT_STRICT);
+
if (read_index_preload(the_index, NULL)  0)
die(_(failed to read the index));
 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 29/45] builtin-am: pass git-apply's options to git-apply

2015-08-04 Thread Paul Tan
git-am.sh recognizes some of git-apply's options, and would pass them to
git-apply:

* --whitespace, since 8c31cb8 (git-am: --whitespace=x option.,
  2006-02-28)

* -C, since 67dad68 (add -C[NUM] to git-am, 2007-02-08)

* -p, since 2092a1f (Teach git-am to pass -p option down to git-apply,
  2007-02-11)

* --directory, since b47dfe9 (git-am: add --directory=dir option,
  2009-01-11)

* --reject, since b80da42 (git-am: implement --reject option passed to
  git-apply, 2009-01-23)

* --ignore-space-change, --ignore-whitespace, since 86c91f9 (git apply:
  option to ignore whitespace differences, 2009-08-04)

* --exclude, since 77e9e49 (am: pass exclude down to apply, 2011-08-03)

* --include, since 58725ef (am: support --include option, 2012-03-28)

* --reject, since b80da42 (git-am: implement --reject option passed to
  git-apply, 2009-01-23)

Re-implement support for these options in builtin/am.c.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 45 +
 1 file changed, 45 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index 727cfb8..f842f69 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -106,6 +106,7 @@ struct am_state {
int keep; /* enum keep_type */
int message_id;
int scissors; /* enum scissors_type */
+   struct argv_array git_apply_opts;
const char *resolvemsg;
int rebasing;
 };
@@ -128,6 +129,8 @@ static void am_state_init(struct am_state *state, const 
char *dir)
git_config_get_bool(am.messageid, state-message_id);
 
state-scissors = SCISSORS_UNSET;
+
+   argv_array_init(state-git_apply_opts);
 }
 
 /**
@@ -140,6 +143,7 @@ static void am_state_release(struct am_state *state)
free(state-author_email);
free(state-author_date);
free(state-msg);
+   argv_array_clear(state-git_apply_opts);
 }
 
 /**
@@ -411,6 +415,11 @@ static void am_load(struct am_state *state)
else
state-scissors = SCISSORS_UNSET;
 
+   read_state_file(sb, state, apply-opt, 1);
+   argv_array_clear(state-git_apply_opts);
+   if (sq_dequote_to_argv_array(sb.buf, state-git_apply_opts)  0)
+   die(_(could not parse %s), am_path(state, apply-opt));
+
state-rebasing = !!file_exists(am_path(state, rebasing));
 
strbuf_release(sb);
@@ -585,6 +594,7 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 {
unsigned char curr_head[GIT_SHA1_RAWSZ];
const char *str;
+   struct strbuf sb = STRBUF_INIT;
 
if (!patch_format)
patch_format = detect_patch_format(paths);
@@ -647,6 +657,9 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
 
write_file(am_path(state, scissors), 1, %s, str);
 
+   sq_quote_argv(sb, state-git_apply_opts.argv, 0);
+   write_file(am_path(state, apply-opt), 1, %s, sb.buf);
+
if (state-rebasing)
write_file(am_path(state, rebasing), 1, %s, );
else
@@ -671,6 +684,8 @@ static void am_setup(struct am_state *state, enum 
patch_format patch_format,
write_file(am_path(state, next), 1, %d, state-cur);
 
write_file(am_path(state, last), 1, %d, state-last);
+
+   strbuf_release(sb);
 }
 
 /**
@@ -1058,6 +1073,8 @@ static int run_apply(const struct am_state *state, const 
char *index_file)
 
argv_array_push(cp.args, apply);
 
+   argv_array_pushv(cp.args, state-git_apply_opts.argv);
+
if (index_file)
argv_array_push(cp.args, --cached);
else
@@ -1084,6 +1101,7 @@ static int build_fake_ancestor(const struct am_state 
*state, const char *index_f
 
cp.git_cmd = 1;
argv_array_push(cp.args, apply);
+   argv_array_pushv(cp.args, state-git_apply_opts.argv);
argv_array_pushf(cp.args, --build-fake-ancestor=%s, index_file);
argv_array_push(cp.args, am_path(state, patch));
 
@@ -1599,9 +1617,36 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 0},
OPT_BOOL('c', scissors, state.scissors,
N_(strip everything before a scissors line)),
+   OPT_PASSTHRU_ARGV(0, whitespace, state.git_apply_opts, 
N_(action),
+   N_(pass it through git-apply),
+   0),
+   OPT_PASSTHRU_ARGV(0, ignore-space-change, 
state.git_apply_opts, NULL,
+   N_(pass it through git-apply),
+   PARSE_OPT_NOARG),
+   OPT_PASSTHRU_ARGV(0, ignore-whitespace, 
state.git_apply_opts, NULL,
+   N_(pass it through git-apply),
+   PARSE_OPT_NOARG),
+   OPT_PASSTHRU_ARGV(0, directory, state.git_apply_opts, 
N_(root),
+   N_(pass it through git-apply),
+   0),
+   OPT_PASSTHRU_ARGV(0, exclude

[PATCH v7 42/45] builtin-am: implement -i/--interactive

2015-08-04 Thread Paul Tan
Since d1c5f2a (Add git-am, applymbox replacement., 2005-10-07),
git-am.sh supported the --interactive mode. After parsing the patch mail
and extracting the patch, commit message and authorship info, an
interactive session will begin that allows the user to choose between:

* applying the patch

* applying the patch and all subsequent patches (by disabling
  interactive mode in subsequent patches)

* skipping the patch

* editing the commit message

Since f89ad67 (Add [v]iew patch in git-am interactive., 2005-10-25),
git-am.sh --interactive also supported viewing the patch to be applied.

When --resolved-ing in --interactive mode, we need to take care to
update the patch with the contents of the index, such that the correct
patch will be displayed when the patch is viewed in interactive mode.

Re-implement the above in builtin/am.c

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 105 ++-
 1 file changed, 104 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 98c10a0..589199f 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -25,6 +25,7 @@
 #include log-tree.h
 #include notes-utils.h
 #include rerere.h
+#include prompt.h
 
 /**
  * Returns 1 if the file is empty or does not exist, 0 otherwise.
@@ -119,6 +120,7 @@ struct am_state {
int prec;
 
/* various operating modes and command line options */
+   int interactive;
int threeway;
int quiet;
int signoff;
@@ -1171,7 +1173,7 @@ static void NORETURN die_user_resolve(const struct 
am_state *state)
if (state-resolvemsg) {
printf_ln(%s, state-resolvemsg);
} else {
-   const char *cmdline = git am;
+   const char *cmdline = state-interactive ? git am -i : git 
am;
 
printf_ln(_(When you have resolved this problem, run \%s 
--continue\.), cmdline);
printf_ln(_(If you prefer to skip this patch, run \%s 
--skip\ instead.), cmdline);
@@ -1404,6 +1406,36 @@ static void write_commit_patch(const struct am_state 
*state, struct commit *comm
 }
 
 /**
+ * Writes the diff of the index against HEAD as a patch to the state
+ * directory's patch file.
+ */
+static void write_index_patch(const struct am_state *state)
+{
+   struct tree *tree;
+   unsigned char head[GIT_SHA1_RAWSZ];
+   struct rev_info rev_info;
+   FILE *fp;
+
+   if (!get_sha1_tree(HEAD, head))
+   tree = lookup_tree(head);
+   else
+   tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+
+   fp = xfopen(am_path(state, patch), w);
+   init_revisions(rev_info, NULL);
+   rev_info.diff = 1;
+   rev_info.disable_stdin = 1;
+   rev_info.no_commit_id = 1;
+   rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
+   rev_info.diffopt.use_color = 0;
+   rev_info.diffopt.file = fp;
+   rev_info.diffopt.close_file = 1;
+   add_pending_object(rev_info, tree-object, );
+   diff_setup_done(rev_info.diffopt);
+   run_diff_index(rev_info, 1);
+}
+
+/**
  * Like parse_mail(), but parses the mail by looking up its commit ID
  * directly. This is used in --rebasing mode to bypass git-mailinfo's munging
  * of patches.
@@ -1655,6 +1687,65 @@ static void validate_resume_state(const struct am_state 
*state)
 }
 
 /**
+ * Interactively prompt the user on whether the current patch should be
+ * applied.
+ *
+ * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to
+ * skip it.
+ */
+static int do_interactive(struct am_state *state)
+{
+   assert(state-msg);
+
+   if (!isatty(0))
+   die(_(cannot be interactive without stdin connected to a 
terminal.));
+
+   for (;;) {
+   const char *reply;
+
+   puts(_(Commit Body is:));
+   puts(--);
+   printf(%s, state-msg);
+   puts(--);
+
+   /*
+* TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+* in your translation. The program will only accept English
+* input at this point.
+*/
+   reply = git_prompt(_(Apply? [y]es/[n]o/[e]dit/[v]iew 
patch/[a]ccept all: ), PROMPT_ECHO);
+
+   if (!reply) {
+   continue;
+   } else if (*reply == 'y' || *reply == 'Y') {
+   return 0;
+   } else if (*reply == 'a' || *reply == 'A') {
+   state-interactive = 0;
+   return 0;
+   } else if (*reply == 'n' || *reply == 'N') {
+   return 1;
+   } else if (*reply == 'e' || *reply == 'E') {
+   struct strbuf msg = STRBUF_INIT;
+
+   if (!launch_editor(am_path(state, final-commit), 
msg, NULL)) {
+   free(state-msg

[PATCH v7 41/45] builtin-am: support and auto-detect mercurial patches

2015-08-04 Thread Paul Tan
Since 0cfd112 (am: preliminary support for hg patches, 2011-08-29),
git-am.sh could convert mercurial patches to an RFC2822 mail patch
suitable for parsing with git-mailinfo, and queue them in the state
directory for application.

Since 15ced75 (git-am foreign patch support: autodetect some patch
formats, 2009-05-27), git-am.sh was able to auto-detect mercurial
patches by checking if the file begins with the line:

# HG changeset patch

Re-implement the above in builtin/am.c.

Helped-by: Stefan Beller sbel...@google.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---

Notes:
v7

* Since a5481a6 (convert enum date_mode into a struct, 2015-06-25),
  show_date() now takes a date_mode struct. Use the DATE_MODE() macro to
  pass the equivalent date_mode struct to show_date().

 builtin/am.c | 74 +++-
 1 file changed, 73 insertions(+), 1 deletion(-)

diff --git a/builtin/am.c b/builtin/am.c
index 3c2ec15..98c10a0 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -81,7 +81,8 @@ enum patch_format {
PATCH_FORMAT_UNKNOWN = 0,
PATCH_FORMAT_MBOX,
PATCH_FORMAT_STGIT,
-   PATCH_FORMAT_STGIT_SERIES
+   PATCH_FORMAT_STGIT_SERIES,
+   PATCH_FORMAT_HG
 };
 
 enum keep_type {
@@ -656,6 +657,11 @@ static int detect_patch_format(const char **paths)
goto done;
}
 
+   if (!strcmp(l1.buf, # HG changeset patch)) {
+   ret = PATCH_FORMAT_HG;
+   goto done;
+   }
+
strbuf_reset(l2);
strbuf_getline_crlf(l2, fp);
strbuf_reset(l3);
@@ -854,6 +860,68 @@ static int split_mail_stgit_series(struct am_state *state, 
const char **paths,
 }
 
 /**
+ * A split_patches_conv() callback that converts a mercurial patch to a RFC2822
+ * message suitable for parsing with git-mailinfo.
+ */
+static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
+{
+   struct strbuf sb = STRBUF_INIT;
+
+   while (!strbuf_getline(sb, in, '\n')) {
+   const char *str;
+
+   if (skip_prefix(sb.buf, # User , str))
+   fprintf(out, From: %s\n, str);
+   else if (skip_prefix(sb.buf, # Date , str)) {
+   unsigned long timestamp;
+   long tz, tz2;
+   char *end;
+
+   errno = 0;
+   timestamp = strtoul(str, end, 10);
+   if (errno)
+   return error(_(invalid timestamp));
+
+   if (!skip_prefix(end,  , str))
+   return error(_(invalid Date line));
+
+   errno = 0;
+   tz = strtol(str, end, 10);
+   if (errno)
+   return error(_(invalid timezone offset));
+
+   if (*end)
+   return error(_(invalid Date line));
+
+   /*
+* mercurial's timezone is in seconds west of UTC,
+* however git's timezone is in hours + minutes east of
+* UTC. Convert it.
+*/
+   tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60;
+   if (tz  0)
+   tz2 = -tz2;
+
+   fprintf(out, Date: %s\n, show_date(timestamp, tz2, 
DATE_MODE(RFC2822)));
+   } else if (starts_with(sb.buf, # )) {
+   continue;
+   } else {
+   fprintf(out, \n%s\n, sb.buf);
+   break;
+   }
+   }
+
+   strbuf_reset(sb);
+   while (strbuf_fread(sb, 8192, in)  0) {
+   fwrite(sb.buf, 1, sb.len, out);
+   strbuf_reset(sb);
+   }
+
+   strbuf_release(sb);
+   return 0;
+}
+
+/**
  * Splits a list of files/directories into individual email patches. Each path
  * in `paths` must be a file/directory that is formatted according to
  * `patch_format`.
@@ -885,6 +953,8 @@ static int split_mail(struct am_state *state, enum 
patch_format patch_format,
return split_mail_conv(stgit_patch_to_mail, state, paths, 
keep_cr);
case PATCH_FORMAT_STGIT_SERIES:
return split_mail_stgit_series(state, paths, keep_cr);
+   case PATCH_FORMAT_HG:
+   return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr);
default:
die(BUG: invalid patch_format);
}
@@ -1937,6 +2007,8 @@ static int parse_opt_patchformat(const struct option 
*opt, const char *arg, int
*opt_value = PATCH_FORMAT_STGIT;
else if (!strcmp(arg, stgit-series))
*opt_value = PATCH_FORMAT_STGIT_SERIES;
+   else if (!strcmp(arg, hg))
+   *opt_value = PATCH_FORMAT_HG;
else
return error(_(Invalid value

[PATCH v7 03/45] builtin-am: implement skeletal builtin am

2015-08-04 Thread Paul Tan
For the purpose of rewriting git-am.sh into a C builtin, implement a
skeletal builtin/am.c that redirects to $GIT_EXEC_PATH/git-am if the
environment variable _GIT_USE_BUILTIN_AM is not defined. Since in the
Makefile git-am.sh takes precedence over builtin/am.c,
$GIT_EXEC_PATH/git-am will contain the shell script git-am.sh, and thus
this allows us to fall back on the functional git-am.sh when running the
test suite for tests that depend on a working git-am implementation.

Since git-am.sh cannot handle any environment modifications by
setup_git_directory(), am is declared with no setup flags in git.c. On
the other hand, to re-implement git-am.sh in builtin/am.c, we need to
run all the git dir and work tree setup logic that git.c typically does
for us. As such, we work around this temporarily by copying the logic in
git.c's run_builtin(), which is roughly:

prefix = setup_git_directory();
trace_repo_setup(prefix);
setup_work_tree();

This redirection should be removed when all the features of git-am.sh
have been re-implemented in builtin/am.c.

Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 Makefile |  1 +
 builtin.h|  1 +
 builtin/am.c | 29 +
 git.c|  6 ++
 4 files changed, 37 insertions(+)
 create mode 100644 builtin/am.c

diff --git a/Makefile b/Makefile
index 7efedbe..da451f8 100644
--- a/Makefile
+++ b/Makefile
@@ -813,6 +813,7 @@ LIB_OBJS += xdiff-interface.o
 LIB_OBJS += zlib.o
 
 BUILTIN_OBJS += builtin/add.o
+BUILTIN_OBJS += builtin/am.o
 BUILTIN_OBJS += builtin/annotate.o
 BUILTIN_OBJS += builtin/apply.o
 BUILTIN_OBJS += builtin/archive.o
diff --git a/builtin.h b/builtin.h
index 839483d..79aaf0a 100644
--- a/builtin.h
+++ b/builtin.h
@@ -30,6 +30,7 @@ extern int textconv_object(const char *path, unsigned mode, 
const unsigned char
 extern int is_builtin(const char *s);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_am(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
 extern int cmd_apply(int argc, const char **argv, const char *prefix);
 extern int cmd_archive(int argc, const char **argv, const char *prefix);
diff --git a/builtin/am.c b/builtin/am.c
new file mode 100644
index 000..fd32caf
--- /dev/null
+++ b/builtin/am.c
@@ -0,0 +1,29 @@
+/*
+ * Builtin git am
+ *
+ * Based on git-am.sh by Junio C Hamano.
+ */
+#include cache.h
+#include builtin.h
+#include exec_cmd.h
+
+int cmd_am(int argc, const char **argv, const char *prefix)
+{
+
+   /*
+* NEEDSWORK: Once all the features of git-am.sh have been
+* re-implemented in builtin/am.c, this preamble can be removed.
+*/
+   if (!getenv(_GIT_USE_BUILTIN_AM)) {
+   const char *path = mkpath(%s/git-am, git_exec_path());
+
+   if (sane_execvp(path, (char **)argv)  0)
+   die_errno(could not exec %s, path);
+   } else {
+   prefix = setup_git_directory();
+   trace_repo_setup(prefix);
+   setup_work_tree();
+   }
+
+   return 0;
+}
diff --git a/git.c b/git.c
index 55c327c..38d9ad5 100644
--- a/git.c
+++ b/git.c
@@ -370,6 +370,12 @@ static int run_builtin(struct cmd_struct *p, int argc, 
const char **argv)
 
 static struct cmd_struct commands[] = {
{ add, cmd_add, RUN_SETUP | NEED_WORK_TREE },
+   /*
+* NEEDSWORK: Once the redirection to git-am.sh in builtin/am.c has
+* been removed, this entry should be changed to
+* RUN_SETUP | NEED_WORK_TREE
+*/
+   { am, cmd_am },
{ annotate, cmd_annotate, RUN_SETUP },
{ apply, cmd_apply, RUN_SETUP_GENTLY },
{ archive, cmd_archive },
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v7 04/45] builtin-am: implement patch queue mechanism

2015-08-04 Thread Paul Tan
git-am applies a series of patches. If the process terminates
abnormally, we want to be able to resume applying the series of patches.
This requires the session state to be saved in a persistent location.

Implement the mechanism of a patch queue, represented by 2 integers --
the index of the current patch we are applying and the index of the last
patch, as well as its lifecycle through the following functions:

* am_setup(), which will set up the state directory
  $GIT_DIR/rebase-apply. As such, even if the process exits abnormally,
  the last-known state will still persist.

* am_load(), which is called if there is an am session in
  progress, to load the last known state from the state directory so we
  can resume applying patches.

* am_run(), which will do the actual patch application. After applying a
  patch, it calls am_next() to increment the current patch index. The
  logic for applying and committing a patch is not implemented yet.

* am_destroy(), which is finally called when we successfully applied all
  the patches in the queue, to clean up by removing the state directory
  and its contents.

Helped-by: Junio C Hamano gits...@pobox.com
Helped-by: Stefan Beller sbel...@google.com
Helped-by: Johannes Schindelin johannes.schinde...@gmx.de
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c | 177 +++
 1 file changed, 177 insertions(+)

diff --git a/builtin/am.c b/builtin/am.c
index fd32caf..ac172c4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -6,9 +6,171 @@
 #include cache.h
 #include builtin.h
 #include exec_cmd.h
+#include parse-options.h
+#include dir.h
+
+struct am_state {
+   /* state directory path */
+   char *dir;
+
+   /* current and last patch numbers, 1-indexed */
+   int cur;
+   int last;
+};
+
+/**
+ * Initializes am_state with the default values. The state directory is set to
+ * dir.
+ */
+static void am_state_init(struct am_state *state, const char *dir)
+{
+   memset(state, 0, sizeof(*state));
+
+   assert(dir);
+   state-dir = xstrdup(dir);
+}
+
+/**
+ * Releases memory allocated by an am_state.
+ */
+static void am_state_release(struct am_state *state)
+{
+   free(state-dir);
+}
+
+/**
+ * Returns path relative to the am_state directory.
+ */
+static inline const char *am_path(const struct am_state *state, const char 
*path)
+{
+   return mkpath(%s/%s, state-dir, path);
+}
+
+/**
+ * Returns 1 if there is an am session in progress, 0 otherwise.
+ */
+static int am_in_progress(const struct am_state *state)
+{
+   struct stat st;
+
+   if (lstat(state-dir, st)  0 || !S_ISDIR(st.st_mode))
+   return 0;
+   if (lstat(am_path(state, last), st) || !S_ISREG(st.st_mode))
+   return 0;
+   if (lstat(am_path(state, next), st) || !S_ISREG(st.st_mode))
+   return 0;
+   return 1;
+}
+
+/**
+ * Reads the contents of `file` in the `state` directory into `sb`. Returns the
+ * number of bytes read on success, -1 if the file does not exist. If `trim` is
+ * set, trailing whitespace will be removed.
+ */
+static int read_state_file(struct strbuf *sb, const struct am_state *state,
+   const char *file, int trim)
+{
+   strbuf_reset(sb);
+
+   if (strbuf_read_file(sb, am_path(state, file), 0) = 0) {
+   if (trim)
+   strbuf_trim(sb);
+
+   return sb-len;
+   }
+
+   if (errno == ENOENT)
+   return -1;
+
+   die_errno(_(could not read '%s'), am_path(state, file));
+}
+
+/**
+ * Loads state from disk.
+ */
+static void am_load(struct am_state *state)
+{
+   struct strbuf sb = STRBUF_INIT;
+
+   if (read_state_file(sb, state, next, 1)  0)
+   die(BUG: state file 'next' does not exist);
+   state-cur = strtol(sb.buf, NULL, 10);
+
+   if (read_state_file(sb, state, last, 1)  0)
+   die(BUG: state file 'last' does not exist);
+   state-last = strtol(sb.buf, NULL, 10);
+
+   strbuf_release(sb);
+}
+
+/**
+ * Removes the am_state directory, forcefully terminating the current am
+ * session.
+ */
+static void am_destroy(const struct am_state *state)
+{
+   struct strbuf sb = STRBUF_INIT;
+
+   strbuf_addstr(sb, state-dir);
+   remove_dir_recursively(sb, 0);
+   strbuf_release(sb);
+}
+
+/**
+ * Setup a new am session for applying patches
+ */
+static void am_setup(struct am_state *state)
+{
+   if (mkdir(state-dir, 0777)  0  errno != EEXIST)
+   die_errno(_(failed to create directory '%s'), state-dir);
+
+   /*
+* NOTE: Since the next and last files determine if an am_state
+* session is in progress, they should be written last.
+*/
+
+   write_file(am_path(state, next), 1, %d, state-cur);
+
+   write_file(am_path(state, last), 1, %d, state-last);
+}
+
+/**
+ * Increments the patch pointer, and cleans am_state for the application

[PATCH v7 00/45] Make git-am a builtin

2015-08-04 Thread Paul Tan
This is a re-roll of [v6]. The changes are as follows:

* removed am.threeWay config to match master

* renamed am_state's append_signoff field to the shorter signoff to
  preserve horizontal space.

* Fix memory leak in am_abort() (Noticed by Stefan, thanks!)

* Rebase onto master, and adjust to the changes introduced by
  a5481a6 (convert enum date_mode into a struct, 2015-06-25).

Interdiff below.

Previous versions:

[WIP v1] http://thread.gmane.org/gmane.comp.version-control.git/270048
[WIP v2] http://thread.gmane.org/gmane.comp.version-control.git/271381
[WIP v3] http://thread.gmane.org/gmane.comp.version-control.git/271967
[v4] http://thread.gmane.org/gmane.comp.version-control.git/272876
[v5] http://thread.gmane.org/gmane.comp.version-control.git/273520
[v6] http://thread.gmane.org/gmane.comp.version-control.git/274225

git-am is a commonly used command for applying a series of patches from a
mailbox to the current branch. Currently, it is implemented by the shell script
git-am.sh. However, compared to C, shell scripts have certain deficiencies:
they need to spawn a lot of processes, introduce a lot of dependencies and
cannot take advantage of git's internal caches.

This patch series rewrites git-am.sh into C builtin/am.c, and is part of my
GSoC project to rewrite git-pull and git-am into C builtins[1].

[1] https://gist.github.com/pyokagan/1b7b0d1f4dab6ba3cef1


Paul Tan (45):
  wrapper: implement xopen()
  wrapper: implement xfopen()
  builtin-am: implement skeletal builtin am
  builtin-am: implement patch queue mechanism
  builtin-am: split out mbox/maildir patches with git-mailsplit
  builtin-am: auto-detect mbox patches
  builtin-am: extract patch and commit info with git-mailinfo
  builtin-am: apply patch with git-apply
  builtin-am: implement committing applied patch
  builtin-am: refuse to apply patches if index is dirty
  builtin-am: implement --resolved/--continue
  builtin-am: don't parse mail when resuming
  builtin-am: implement --skip
  builtin-am: implement --abort
  builtin-am: reject patches when there's a session in progress
  builtin-am: implement -q/--quiet
  builtin-am: exit with user friendly message on failure
  builtin-am: implement -s/--signoff
  cache-tree: introduce write_index_as_tree()
  builtin-am: implement --3way
  builtin-am: implement --rebasing mode
  builtin-am: bypass git-mailinfo when --rebasing
  builtin-am: handle stray state directory
  builtin-am: implement -u/--utf8
  builtin-am: implement -k/--keep, --keep-non-patch
  builtin-am: implement --[no-]message-id, am.messageid
  builtin-am: support --keep-cr, am.keepcr
  builtin-am: implement --[no-]scissors
  builtin-am: pass git-apply's options to git-apply
  builtin-am: implement --ignore-date
  builtin-am: implement --committer-date-is-author-date
  builtin-am: implement -S/--gpg-sign, commit.gpgsign
  builtin-am: invoke post-rewrite hook
  builtin-am: support automatic notes copying
  builtin-am: invoke applypatch-msg hook
  builtin-am: invoke pre-applypatch hook
  builtin-am: invoke post-applypatch hook
  builtin-am: rerere support
  builtin-am: support and auto-detect StGit patches
  builtin-am: support and auto-detect StGit series files
  builtin-am: support and auto-detect mercurial patches
  builtin-am: implement -i/--interactive
  builtin-am: implement legacy -b/--binary option
  builtin-am: check for valid committer ident
  builtin-am: remove redirection to git-am.sh

 Makefile|2 +-
 builtin.h   |1 +
 builtin/am.c| 2319 +++
 cache-tree.c|   29 +-
 cache-tree.h|1 +
 git-am.sh = contrib/examples/git-am.sh |0
 git-compat-util.h   |2 +
 git.c   |1 +
 wrapper.c   |   56 +
 9 files changed, 2398 insertions(+), 13 deletions(-)
 create mode 100644 builtin/am.c
 rename git-am.sh = contrib/examples/git-am.sh (100%)

diff --git a/builtin/am.c b/builtin/am.c
index 1116304..84d57d4 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -123,7 +123,7 @@ struct am_state {
int interactive;
int threeway;
int quiet;
-   int append_signoff;
+   int signoff;
int utf8;
int keep; /* enum keep_type */
int message_id;
@@ -152,8 +152,6 @@ static void am_state_init(struct am_state *state, const 
char *dir)
 
state-prec = 4;
 
-   git_config_get_bool(am.threeway, state-threeway);
-
state-utf8 = 1;
 
git_config_get_bool(am.messageid, state-message_id);
@@ -429,7 +427,7 @@ static void am_load(struct am_state *state)
state-quiet = !strcmp(sb.buf, t);
 
read_state_file(sb, state, sign, 1);
-   state-append_signoff = !strcmp(sb.buf, t);
+   state-signoff = !strcmp(sb.buf, t);
 
read_state_file(sb, state, utf8, 1);
state-utf8 = !strcmp(sb.buf

[PATCH v2 0/3] am: let command-line options override saved options

2015-08-04 Thread Paul Tan
Let command-line options override saved options in git-am when resuming

This is a re-roll of [v1]. Previous versions:

[v1] http://thread.gmane.org/gmane.comp.version-control.git/274789

When resuming, git-am mistakenly ignores command-line options.

For instance, when a patch fails to apply with git am patch, subsequently
running git am --3way would not cause git-am to fall back on attempting a
threeway merge.  This occurs because by default the --3way option is saved as
false, and the saved am options are loaded after the command-line options are
parsed, thus overwriting the command-line options when resuming.

[PATCH 1/3] tweaks test-terminal.perl to redirect the stdin of the child
process to a pty. This is to support the tests in [PATCH 2/3].

[PATCH 2/3] fixes builtin/am.c, enabling command-line options to override saved
options. However, even with this patch, the following command-line options have
no effect when resuming:

* --signoff overriding --no-signoff

* --no-keep overriding --keep

* --message-id overriding --no-message-id

* --scissors overriding --no-scissors

This is because they are only taken into account during the mail-parsing stage,
which is skipped over when resuming.

[PATCH 3/3] adds support for the --signoff option when resuming by recognizing
that we can (re-)append the signoff when the user explicitly specifies the
--signoff option.

Since the --keep, --message-id and --scissors options are handled by
git-mailinfo, it is tricky to implement support for them without introducing
lots of code complexity, and thus this patch series does not attempt to.

Furthermore, it is hard to imagine a use case for e.g. --scissors overriding
--no-scissors, and hence it might be preferable to wait until someone comes
with a solid use case, instead of implementing potentially undesirable behavior
and having to support it.


Paul Tan (3):
  test_terminal: redirect child process' stdin to a pty
  am: let command-line options override saved options
  am: let --signoff override --no-signoff

 builtin/am.c   |  42 ---
 t/t4153-am-resume-override-opts.sh | 102 +
 t/test-terminal.perl   |  25 +++--
 3 files changed, 158 insertions(+), 11 deletions(-)
 create mode 100755 t/t4153-am-resume-override-opts.sh

-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 1/3] test_terminal: redirect child process' stdin to a pty

2015-08-04 Thread Paul Tan
When resuming, git-am detects if we are trying to feed it patches or not
by checking if stdin is a TTY.

However, the test library redirects stdin to /dev/null. This makes it
difficult, for instance, to test the behavior of git am -3 when
resuming, as git-am will think we are trying to feed it patches and
error out.

Support this use case by extending test-terminal.perl to create a
pseudo-tty for the child process' standard input as well.

Note that due to the way the code is structured, the child's stdin
pseudo-tty will be closed when we finish reading from our stdin. This
means that in the common case, where our stdin is attached to /dev/null,
the child's stdin pseudo-tty will be closed immediately. Some operations
like isatty(), which git-am uses, require the file descriptor to be
open, and hence if the success of the command depends on such functions,
test_terminal's stdin should be redirected to a source with large amount
of data to ensure that the child's stdin is not closed, e.g.

test_terminal git am --3way /dev/zero

Cc: Jonathan Nieder jrnie...@gmail.com
Cc: Jeff King p...@peff.net
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 t/test-terminal.perl | 25 -
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/t/test-terminal.perl b/t/test-terminal.perl
index 1fb373f..f6fc9ae 100755
--- a/t/test-terminal.perl
+++ b/t/test-terminal.perl
@@ -5,15 +5,17 @@ use warnings;
 use IO::Pty;
 use File::Copy;
 
-# Run @$argv in the background with stdio redirected to $out and $err.
+# Run @$argv in the background with stdio redirected to $in, $out and $err.
 sub start_child {
-   my ($argv, $out, $err) = @_;
+   my ($argv, $in, $out, $err) = @_;
my $pid = fork;
if (not defined $pid) {
die fork failed: $!
} elsif ($pid == 0) {
+   open STDIN, , $in;
open STDOUT, , $out;
open STDERR, , $err;
+   close $in;
close $out;
exec(@$argv) or die cannot exec '$argv-[0]': $!
}
@@ -50,14 +52,23 @@ sub xsendfile {
 }
 
 sub copy_stdio {
-   my ($out, $err) = @_;
+   my ($in, $out, $err) = @_;
my $pid = fork;
+   if (!$pid) {
+   close($out);
+   close($err);
+   xsendfile($in, \*STDIN);
+   exit 0;
+   }
+   $pid = fork;
defined $pid or die fork failed: $!;
if (!$pid) {
+   close($in);
close($out);
xsendfile(\*STDERR, $err);
exit 0;
}
+   close($in);
close($err);
xsendfile(\*STDOUT, $out);
finish_child($pid) == 0
@@ -67,14 +78,18 @@ sub copy_stdio {
 if ($#ARGV  1) {
die usage: test-terminal program args;
 }
+my $master_in = new IO::Pty;
 my $master_out = new IO::Pty;
 my $master_err = new IO::Pty;
+$master_in-set_raw();
 $master_out-set_raw();
 $master_err-set_raw();
+$master_in-slave-set_raw();
 $master_out-slave-set_raw();
 $master_err-slave-set_raw();
-my $pid = start_child(\@ARGV, $master_out-slave, $master_err-slave);
+my $pid = start_child(\@ARGV, $master_in-slave, $master_out-slave, 
$master_err-slave);
+close $master_in-slave;
 close $master_out-slave;
 close $master_err-slave;
-copy_stdio($master_out, $master_err);
+copy_stdio($master_in, $master_out, $master_err);
 exit(finish_child($pid));
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 2/3] am: let command-line options override saved options

2015-08-04 Thread Paul Tan
When resuming, git-am mistakenly ignores command-line options.

For instance, when a patch fails to apply with git am patch,
subsequently running git am --3way would not cause git-am to fall
back on attempting a threeway merge.  This occurs because by default
the --3way option is saved as false, and the saved am options are
loaded after the command-line options are parsed, thus overwriting
the command-line options when resuming.

Fix this by moving the am_load() function call before parse_options(),
so that command-line options will override the saved am options.

The purpose of supporting this use case is to enable users to wiggle
that one conflicting patch. As such, it is expected that the
command-line options do not affect subsequent applied patches. Implement
this by calling am_load() once we apply the conflicting patch
successfully.

Noticed-by: Junio C Hamano gits...@pobox.com
Helped-by: Junio C Hamano gits...@pobox.com
Helped-by: Jeff King p...@peff.net
Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c   | 16 ++--
 t/t4153-am-resume-override-opts.sh | 82 ++
 2 files changed, 94 insertions(+), 4 deletions(-)
 create mode 100755 t/t4153-am-resume-override-opts.sh

diff --git a/builtin/am.c b/builtin/am.c
index 84d57d4..0961304 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1777,7 +1777,6 @@ static void am_run(struct am_state *state, int resume)
 
if (resume) {
validate_resume_state(state);
-   resume = 0;
} else {
int skip;
 
@@ -1839,6 +1838,10 @@ static void am_run(struct am_state *state, int resume)
 
 next:
am_next(state);
+
+   if (resume)
+   am_load(state);
+   resume = 0;
}
 
if (!is_empty_file(am_path(state, rewritten))) {
@@ -1893,6 +1896,7 @@ static void am_resolve(struct am_state *state)
 
 next:
am_next(state);
+   am_load(state);
am_run(state, 0);
 }
 
@@ -2020,6 +2024,7 @@ static void am_skip(struct am_state *state)
die(_(failed to clean index));
 
am_next(state);
+   am_load(state);
am_run(state, 0);
 }
 
@@ -2130,6 +2135,7 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
int keep_cr = -1;
int patch_format = PATCH_FORMAT_UNKNOWN;
enum resume_mode resume = RESUME_FALSE;
+   int in_progress;
 
const char * const usage[] = {
N_(git am [options] [(mbox|Maildir)...]),
@@ -2225,6 +2231,10 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
 
am_state_init(state, git_path(rebase-apply));
 
+   in_progress = am_in_progress(state);
+   if (in_progress)
+   am_load(state);
+
argc = parse_options(argc, argv, prefix, options, usage, 0);
 
if (binary = 0)
@@ -2237,7 +2247,7 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
if (read_index_preload(the_index, NULL)  0)
die(_(failed to read the index));
 
-   if (am_in_progress(state)) {
+   if (in_progress) {
/*
 * Catch user error to feed us patches when there is a session
 * in progress:
@@ -2255,8 +2265,6 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
 
if (resume == RESUME_FALSE)
resume = RESUME_APPLY;
-
-   am_load(state);
} else {
struct argv_array paths = ARGV_ARRAY_INIT;
int i;
diff --git a/t/t4153-am-resume-override-opts.sh 
b/t/t4153-am-resume-override-opts.sh
new file mode 100755
index 000..39fac79
--- /dev/null
+++ b/t/t4153-am-resume-override-opts.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+test_description='git-am command-line options override saved options'
+
+. ./test-lib.sh
+. $TEST_DIRECTORY/lib-terminal.sh
+
+format_patch () {
+   git format-patch --stdout -1 $1 $1.eml
+}
+
+test_expect_success 'setup' '
+   test_commit initial file 
+   test_commit first file 
+
+   git checkout initial 
+   git mv file file2 
+   test_tick 
+   git commit -m renamed-file 
+   git tag renamed-file 
+
+   git checkout -b side initial 
+   test_commit side1 file 
+   test_commit side2 file 
+
+   format_patch side1 
+   format_patch side2
+'
+
+test_expect_success TTY '--3way overrides --no-3way' '
+   rm -fr .git/rebase-apply 
+   git reset --hard 
+   git checkout renamed-file 
+
+   # Applying side1 will fail as the file has been renamed.
+   test_must_fail git am --no-3way side[12].eml 
+   test_path_is_dir .git/rebase-apply 
+   test_cmp_rev renamed-file HEAD 
+   test -z $(git ls-files -u) 
+
+   # Applying side1 with am --3way will succeed due to the threeway-merge.
+   # Applying side2 will fail as --3way does not apply

[PATCH v2 3/3] am: let --signoff override --no-signoff

2015-08-04 Thread Paul Tan
After resolving a conflicting patch, a user may wish to sign off the
patch to declare that the patch has been modified. As such, the user
will expect that running git am --signoff --continue will append the
signoff to the commit message.

However, the --signoff option is only taken into account during the
mail-parsing stage. If the --signoff option is set, then the signoff
will be appended to the commit message. Since the mail-parsing stage
comes before the patch application stage, the --signoff option, if
provided on the command-line when resuming, will have no effect at all.

We cannot move the append_signoff() call to the patch application stage
as the applypatch-msg hook and interactive mode, which run before patch
application, may expect the signoff to be there.

Fix this by taking note if the user explictly set the --signoff option
on the command-line, and append the signoff to the commit message when
resuming if so.

Signed-off-by: Paul Tan pyoka...@gmail.com
---
 builtin/am.c   | 28 +---
 t/t4153-am-resume-override-opts.sh | 20 
 2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/builtin/am.c b/builtin/am.c
index 0961304..8c95aec 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -98,6 +98,12 @@ enum scissors_type {
SCISSORS_TRUE/* pass --scissors to git-mailinfo */
 };
 
+enum signoff_type {
+   SIGNOFF_FALSE = 0,
+   SIGNOFF_TRUE = 1,
+   SIGNOFF_EXPLICIT /* --signoff was set on the command-line */
+};
+
 struct am_state {
/* state directory path */
char *dir;
@@ -123,7 +129,7 @@ struct am_state {
int interactive;
int threeway;
int quiet;
-   int signoff;
+   int signoff; /* enum signoff_type */
int utf8;
int keep; /* enum keep_type */
int message_id;
@@ -1184,6 +1190,18 @@ static void NORETURN die_user_resolve(const struct 
am_state *state)
 }
 
 /**
+ * Appends signoff to the msg field of the am_state.
+ */
+static void am_append_signoff(struct am_state *state)
+{
+   struct strbuf sb = STRBUF_INIT;
+
+   strbuf_attach(sb, state-msg, state-msg_len, state-msg_len);
+   append_signoff(sb, 0, 0);
+   state-msg = strbuf_detach(sb, state-msg_len);
+}
+
+/**
  * Parses `mail` using git-mailinfo, extracting its patch and authorship info.
  * state-msg will be set to the patch message. state-author_name,
  * state-author_email and state-author_date will be set to the patch author's
@@ -2151,8 +2169,9 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
OPT_BOOL('3', 3way, state.threeway,
N_(allow fall back on 3way merging if needed)),
OPT__QUIET(state.quiet, N_(be quiet)),
-   OPT_BOOL('s', signoff, state.signoff,
-   N_(add a Signed-off-by line to the commit message)),
+   OPT_SET_INT('s', signoff, state.signoff,
+   N_(add a Signed-off-by line to the commit message),
+   SIGNOFF_EXPLICIT),
OPT_BOOL('u', utf8, state.utf8,
N_(recode into utf8 (default))),
OPT_SET_INT('k', keep, state.keep,
@@ -2265,6 +2284,9 @@ int cmd_am(int argc, const char **argv, const char 
*prefix)
 
if (resume == RESUME_FALSE)
resume = RESUME_APPLY;
+
+   if (state.signoff == SIGNOFF_EXPLICIT)
+   am_append_signoff(state);
} else {
struct argv_array paths = ARGV_ARRAY_INIT;
int i;
diff --git a/t/t4153-am-resume-override-opts.sh 
b/t/t4153-am-resume-override-opts.sh
index 39fac79..7c013d8 100755
--- a/t/t4153-am-resume-override-opts.sh
+++ b/t/t4153-am-resume-override-opts.sh
@@ -64,6 +64,26 @@ test_expect_success '--no-quiet overrides --quiet' '
test_i18ncmp expected out
 '
 
+test_expect_success '--signoff overrides --no-signoff' '
+   rm -fr .git/rebase-apply 
+   git reset --hard 
+   git checkout first 
+
+   test_must_fail git am --no-signoff side[12].eml 
+   test_path_is_dir .git/rebase-apply 
+   echo side1 file 
+   git add file 
+   git am --signoff --continue 
+
+   # Applied side1 will be signed off
+   echo Signed-off-by: $GIT_COMMITTER_NAME $GIT_COMMITTER_EMAIL 
expected 
+   git cat-file commit HEAD^ | grep Signed-off-by: actual 
+   test_cmp expected actual 
+
+   # Applied side2 will not be signed off
+   test $(git cat-file commit HEAD | grep -c Signed-off-by:) -eq 0
+'
+
 test_expect_success TTY '--reject overrides --no-reject' '
rm -fr .git/rebase-apply 
git reset --hard 
-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2 0/3] am: let command-line options override saved options

2015-08-04 Thread Paul Tan
Let command-line options override saved options in git-am when resuming

This is a re-roll of [v1]. Previous versions:

[v1] http://thread.gmane.org/gmane.comp.version-control.git/274789

When resuming, git-am mistakenly ignores command-line options.

For instance, when a patch fails to apply with git am patch, subsequently
running git am --3way would not cause git-am to fall back on attempting a
threeway merge.  This occurs because by default the --3way option is saved as
false, and the saved am options are loaded after the command-line options are
parsed, thus overwriting the command-line options when resuming.

[PATCH 1/3] tweaks test-terminal.perl to redirect the stdin of the child
process to a pty. This is to support the tests in [PATCH 2/3].

[PATCH 2/3] fixes builtin/am.c, enabling command-line options to override saved
options. However, even with this patch, the following command-line options have
no effect when resuming:

* --signoff overriding --no-signoff

* --no-keep overriding --keep

* --message-id overriding --no-message-id

* --scissors overriding --no-scissors

This is because they are only taken into account during the mail-parsing stage,
which is skipped over when resuming.

[PATCH 3/3] adds support for the --signoff option when resuming by recognizing
that we can (re-)append the signoff when the user explicitly specifies the
--signoff option.

Since the --keep, --message-id and --scissors options are handled by
git-mailinfo, it is tricky to implement support for them without introducing
lots of code complexity, and thus this patch series does not attempt to.

Furthermore, it is hard to imagine a use case for e.g. --scissors overriding
--no-scissors, and hence it might be preferable to wait until someone comes
with a solid use case, instead of implementing potentially undesirable behavior
and having to support it.


Paul Tan (3):
  test_terminal: redirect child process' stdin to a pty
  am: let command-line options override saved options
  am: let --signoff override --no-signoff

 builtin/am.c   |  42 ---
 t/t4153-am-resume-override-opts.sh | 102 +
 t/test-terminal.perl   |  25 +++--
 3 files changed, 158 insertions(+), 11 deletions(-)
 create mode 100755 t/t4153-am-resume-override-opts.sh

-- 
2.5.0.280.gd88bd6e

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


  1   2   3   4   5   6   7   >