* Re: [PATCH 6/6] rm: add absorb a submodules git dir before deletion
From: Stefan Beller @ 2016-12-13 17:51 UTC (permalink / raw)
To: brian m. carlson, Stefan Beller, Junio C Hamano,
git@vger.kernel.org, David Turner, Brandon Williams
In-Reply-To: <20161213032848.4ps42jinix6fdgdc@genre.crustytoothpaste.net>
On Mon, Dec 12, 2016 at 7:28 PM, brian m. carlson
<sandals@crustytoothpaste.net> wrote:
> On Mon, Dec 12, 2016 at 05:40:55PM -0800, Stefan Beller wrote:
>> When deleting a submodule we need to keep the actual git directory around,
>> such that we do not lose local changes in there and at a later checkout
>> of the submodule we don't need to clone it again.
>>
>> Implement `depopulate_submodule`, that migrates the git directory before
>> deletion of a submodule and afterwards the equivalent of "rm -rf", which
>> is already found in entry.c, so expose that and for clarity add a suffix
>> "_or_dir" to it.
>
> I think you might have meant "_or_die" here.
indeed, will fix in a reroll. Thanks for the review!
^ permalink raw reply
* Re: git add -p with new file
From: Jeff King @ 2016-12-13 17:33 UTC (permalink / raw)
To: Stephan Beyer; +Cc: Junio C Hamano, Ariel, git
In-Reply-To: <dc698b79-6311-a2a3-c564-a43ef071e62b@gmx.net>
On Mon, Dec 12, 2016 at 09:31:03PM +0100, Stephan Beyer wrote:
> I am also a "git add -p"-only user (except for new files and merges).
> However, I usually keep a lot of untracked files in my repositories.
> Files that I do not (git)ignore because I want to see them when I type
> "git status".
>
> Hence, the imagination only that "git add -p" starts to ask me for each
> untracked file feels like a lot of annoying "n" presses. I could imagine
> that it is okay-ish when it asks about the untracked files *after* all
> tracked paths have been processed (I guess this has been proposed
> before), so that I can safely quit.
Yeah, this is the "some people might be annoyed" that I mentioned
originally. If your workflow leaves a lot of untracked files that you
don't care about it, then I think you'd want this feature disabled
entirely via a config option (or vice versa, that it would only be
enabled by config option).
> I am also not really sure what problem this feature is trying to solve.
> If the "problem"(?) is that it should act more like "git add" instead of
> "git add -u", for whatever reason, this may be fine (but the
> configuration option is a must-have then).
I think the problem is just that "add -p" does not give the whole story
of what you might want to do before making a commit.
> > I'd also probably add interactive.showUntracked to make the whole thing
> > optional (but I think it would be OK to default it to on).
> Hm, "interactive.showUntracked" is a confusing name because "git add -i"
> (interactive) already handles untracked files.
Sure, that was just meant for illustration. I agree there's probably a
better name.
-Peff
^ permalink raw reply
* Re: [RFC/PATCH v3 00/16] Add initial experimental external ODB support
From: Christian Couder @ 2016-12-13 17:20 UTC (permalink / raw)
To: Lars Schneider
Cc: git, Junio C Hamano, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey,
Eric Wong, Christian Couder
In-Reply-To: <A5ABBF3E-BED9-4FF3-9DE5-B529DEF0B8E8@gmail.com>
On Sat, Dec 3, 2016 at 7:47 PM, Lars Schneider <larsxschneider@gmail.com> wrote:
>
>> On 30 Nov 2016, at 22:04, Christian Couder <christian.couder@gmail.com> wrote:
>>
>> Goal
>> ~~~~
>>
>> Git can store its objects only in the form of loose objects in
>> separate files or packed objects in a pack file.
>>
>> To be able to better handle some kind of objects, for example big
>> blobs, it would be nice if Git could store its objects in other object
>> databases (ODB).
>
> This is a great goal. I really hope we can use that to solve the
> pain points in the current Git <--> GitLFS integration!
Yeah, I hope it will help too.
> Thanks for working on this!
>
> Minor nit: I feel the term "other" could be more expressive. Plus
> "database" might confuse people. What do you think about
> "External Object Storage" or something?
In the current Git code, "DB" is already used a lot. For example in
cache.h there is:
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
#define INIT_DB_QUIET 0x0001
#define INIT_DB_EXIST_OK 0x0002
extern int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, unsigned int flags);
[...]
>> - "<command> get <sha1>": the command should then read from the
>> external ODB the content of the object corresponding to <sha1> and
>> output it on stdout.
>>
>> - "<command> put <sha1> <size> <type>": the command should then read
>> from stdin an object and store it in the external ODB.
>
> Based on my experience with Git clean/smudge filters I think this kind
> of single shot protocol will be a performance bottleneck as soon as
> people store more than >1000 files in the external ODB.
> Maybe you can reuse my "filter process protocol" (edcc858) here?
Yeah, I would like to do reuse your "filter process protocol" as much
as possible to improve this in the future.
>> * Transfer
>>
>> To tranfer information about the blobs stored in external ODB, some
>> special refs, called "odb ref", similar as replace refs, are used.
>>
>> For now there should be one odb ref per blob. Each ref name should be
>> refs/odbs/<odbname>/<sha1> where <sha1> is the sha1 of the blob stored
>> in the external odb named <odbname>.
>>
>> These odb refs should all point to a blob that should be stored in the
>> Git repository and contain information about the blob stored in the
>> external odb. This information can be specific to the external odb.
>> The repos can then share this information using commands like:
>>
>> `git fetch origin "refs/odbs/<odbname>/*:refs/odbs/<odbname>/*"`
>
> The "odbref" would point to a blob and the blob could contain anything,
> right? E.g. it could contain an existing GitLFS pointer, right?
>
> version https://git-lfs.github.com/spec/v1
> oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
> size 12345
Yes, but I think that the sha1 should be added. So yes, it could
easily be made compatible with git LFS.
>> Design discussion about performance
>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>
>> Yeah, it is not efficient to fork/exec a command to just read or write
>> one object to or from the external ODB. Batch calls and/or using a
>> daemon and/or RPC should be used instead to be able to store regular
>> objects in an external ODB. But for now the external ODB would be all
>> about really big files, where the cost of a fork+exec should not
>> matter much. If we later want to extend usage of external ODBs, yeah
>> we will probably need to design other mechanisms.
>
> I think we should leverage the learnings from GitLFS as much as possible.
> My learnings are:
>
> (1) Fork/exec per object won't work. People have lots and lots of content
> that is not suited for Git (e.g. integration test data, images, ...).
I agree that it will not work for many people, but look at how git LFS
evolved. It first started without a good solution for those people,
and then you provided a much better solution to them.
So I am a bit reluctant to work on a complex solution reusing your
"filter process protocol" work right away.
> (2) We need a good UI. I think it would be great if the average user would
> not even need to know about ODB. Moving files explicitly with a "put"
> command seems unpractical to me. GitLFS tracks files via filename and
> that has a number of drawbacks, too. Do you see a way to define a
> customizable metric such as "move all files to ODB X that are gzip
> compressed larger than Y"?
I think these should be defined in the config and attributes files. It
could also be possible to implement a "want" command (in the same way
as the "get", "put" and "have" commands) to ask the e-odb helper if it
wants to store a specific blob.
>> Future work
>> ~~~~~~~~~~~
>>
>> I think that the odb refs don't prevent a regular fetch or push from
>> wanting to send the objects that are managed by an external odb. So I
>> am interested in suggestions about this problem. I will take a look at
>> previous discussions and how other mechanisms (shallow clone, bundle
>> v3, ...) handle this.
>
> If the ODB configuration is stored in the Git repo similar to
> .gitmodules then every client that clones ODB references would be able
> to resolve them, right?
Yeah, but I am not sure that being able to resolve the odb refs will
prevent the big blobs from being sent.
With Git LFS, git doesn't know about the big blobs, only about the
substituted files, but that is not the case in what I am doing.
Thanks,
Christian.
^ permalink raw reply
* Re: [RFC/PATCH v3 00/16] Add initial experimental external ODB support
From: Christian Couder @ 2016-12-13 16:40 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Jeff King, Nguyen Thai Ngoc Duy, Mike Hommey, Lars Schneider,
Eric Wong, Christian Couder
In-Reply-To: <xmqqy400bno3.fsf@gitster.mtv.corp.google.com>
On Wed, Nov 30, 2016 at 11:36 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Christian Couder <christian.couder@gmail.com> writes:
>
>> For now there should be one odb ref per blob. Each ref name should be
>> refs/odbs/<odbname>/<sha1> where <sha1> is the sha1 of the blob stored
>> in the external odb named <odbname>.
>>
>> These odb refs should all point to a blob that should be stored in the
>> Git repository and contain information about the blob stored in the
>> external odb. This information can be specific to the external odb.
>> The repos can then share this information using commands like:
>>
>> `git fetch origin "refs/odbs/<odbname>/*:refs/odbs/<odbname>/*"`
>
> Unless this is designed to serve only a handful of blobs, I cannot
> see how this design would scale successfully. I notice you wrote
> "For now" at the beginning, but what is the envisioned way this will
> evolve in the future?
In general I think that having a lot of refs is really a big problem
right now in Git as many big organizations using Git are facing this
problem in one form or another.
So I think that support for a big number of refs is a separate and
important problem that should and hopefully will be solved.
My preferred way to solve it would be with something like Shawn's
RefTree. I think it would also help regarding other problems like
speeding up git protocol, tracking patch series (see git-series
discussions), tools like https://www.softwareheritage.org/, ...
If the "big number of refs" problem is not solved and many refs in
refs/odbs/<odbname>/ is a problem, it's possible to have just one ref
in refs/odbs/<odbname>/ that points to a blob that contains a list
(maybe a json list with information attached to each item) of the
blobs stored in the external odb. Though I think it would be more
complex to edit/parse this list than to deal with many refs in
refs/odbs/<odbname>/.
^ permalink raw reply
* Re: What's cooking in git.git (Dec 2016, #02; Mon, 12)
From: Junio C Hamano @ 2016-12-13 16:13 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <alpine.DEB.2.20.1612131641291.23160@virtualbox>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> While I think it would make it easier for people to experiment and
>> build on if the topic is merged to 'next', I am at the same time a
>> bit reluctant to merge an unproven new topic that introduces a new
>> file format, which we may end up having to support til the end of
>> time. It is likely that to support a "prime clone from CDN", it
>> would need a lot more than just "these are the heads and the pack
>> data is over there", so this may not be sufficient.
>>
>> Will discard.
>
> You could mark it as experimental, subject to change, and merge it to
> `next` safely.
Are you planning, or do you know somebody who plans to use that code
soonish? Otherwise I'd prefer to drop it---at this point, the series
is merely "just because we can", not "because we need it to further
improve this or that".
^ permalink raw reply
* Re: What's cooking in git.git (Dec 2016, #02; Mon, 12)
From: Junio C Hamano @ 2016-12-13 16:09 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git
In-Reply-To: <alpine.DEB.2.20.1612131638290.23160@virtualbox>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Hi Junio,
>
> On Mon, 12 Dec 2016, Junio C Hamano wrote:
>
>> * js/mingw-isatty (2016-12-11) 1 commit
>> (merged to 'next' on 2016-12-12 at 60c1da6676)
>> + mingw: intercept isatty() to handle /dev/null as Git expects it
>>
>> We often decide if a session is interactive by checking if the
>> standard I/O streams are connected to a TTY, but isatty() emulation
>> on Windows incorrectly returned true if it is used on NUL (i.e. an
>> equivalent to /dev/null). This has been fixed.
>
> I'd like to suggest a reword: we did not use an isatty() emulation, but
> Windows' own _isatty() function that simply has different semantics than
> what Git expected. *Now* we have an isatty() emulation that wraps
> _isatty() and emulates the behavior expected by Git.
Thanks for a comment.
One of the things that the new code does with the fix is this:
+/* In this file, we actually want to use Windows' own isatty(). */
+#undef isatty
+
which undoes "#define isatty winansi_isatty" that other code uses,
so that the implementation of winansi_isatty() can say isatty() and
get what people usually get when they say "isatty()" on Windows.
Before or after that patch, there is no "#define isatty _isatty" in
our codebase. I take all of the above to mean that Windows does
give us isatty() function (not a macro--as otherwise it won't become
available to us again by "#undef isatty"), that in turn internally
calls what it calls _isatty() that says true for NUL?
Following the above reasoning, I meant "whatever you get when you
write isatty() on Windows" by "isatty() emulation on Windows" in the
paragraph you are commenting on. I didn't say "what was written by
Git for Windows folks to emulate isatty()" or "what was given by MS
development tools", as that distinction is immaterial and does not
change the value of the fix.
^ permalink raw reply
* Re: What's cooking in git.git (Dec 2016, #02; Mon, 12)
From: Johannes Schindelin @ 2016-12-13 15:42 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqoa0g96o3.fsf@gitster.mtv.corp.google.com>
Hi Junio,
On Mon, 12 Dec 2016, Junio C Hamano wrote:
> * jc/bundle (2016-03-03) 6 commits
> - index-pack: --clone-bundle option
> - Merge branch 'jc/index-pack' into jc/bundle
> - bundle v3: the beginning
> - bundle: keep a copy of bundle file name in the in-core bundle header
> - bundle: plug resource leak
> - bundle doc: 'verify' is not about verifying the bundle
>
> The beginning of "split bundle", which could be one of the
> ingredients to allow "git clone" traffic off of the core server
> network to CDN.
>
> While I think it would make it easier for people to experiment and
> build on if the topic is merged to 'next', I am at the same time a
> bit reluctant to merge an unproven new topic that introduces a new
> file format, which we may end up having to support til the end of
> time. It is likely that to support a "prime clone from CDN", it
> would need a lot more than just "these are the heads and the pack
> data is over there", so this may not be sufficient.
>
> Will discard.
You could mark it as experimental, subject to change, and merge it to
`next` safely.
Ciao,
Dscho
^ permalink raw reply
* Re: What's cooking in git.git (Dec 2016, #02; Mon, 12)
From: Johannes Schindelin @ 2016-12-13 15:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqoa0g96o3.fsf@gitster.mtv.corp.google.com>
Hi Junio,
On Mon, 12 Dec 2016, Junio C Hamano wrote:
> * js/mingw-isatty (2016-12-11) 1 commit
> (merged to 'next' on 2016-12-12 at 60c1da6676)
> + mingw: intercept isatty() to handle /dev/null as Git expects it
>
> We often decide if a session is interactive by checking if the
> standard I/O streams are connected to a TTY, but isatty() emulation
> on Windows incorrectly returned true if it is used on NUL (i.e. an
> equivalent to /dev/null). This has been fixed.
I'd like to suggest a reword: we did not use an isatty() emulation, but
Windows' own _isatty() function that simply has different semantics than
what Git expected. *Now* we have an isatty() emulation that wraps
_isatty() and emulates the behavior expected by Git.
Thanks,
Dscho
^ permalink raw reply
* [PATCH v2 29/34] sequencer (rebase -i): show only failed `git commit`'s output
From: Johannes Schindelin @ 2016-12-13 15:32 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
This is the behavior of the shell script version of the interactive
rebase, by using the `output` function defined in `git-rebase.sh`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 63f6f25ced..dfa4fab98b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -647,10 +647,15 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
{
char **env = NULL;
struct argv_array array;
- int rc;
+ int opt = RUN_GIT_CMD, rc;
const char *value;
if (is_rebase_i(opts)) {
+ if (!edit) {
+ opt |= RUN_COMMAND_STDOUT_TO_STDERR;
+ opt |= RUN_HIDE_STDERR_ON_SUCCESS;
+ }
+
env = read_author_script();
if (!env) {
const char *gpg_opt = gpg_sign_opt_quoted(opts);
@@ -687,7 +692,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
if (opts->allow_empty_message)
argv_array_push(&array, "--allow-empty-message");
- rc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,
+ rc = run_command_v_opt_cd_env(array.argv, opt, NULL,
(const char *const *)env);
argv_array_clear(&array);
free(env);
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 30/34] sequencer (rebase -i): show only failed cherry-picks' output
From: Johannes Schindelin @ 2016-12-13 15:32 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
This is the behavior of the shell script version of the interactive
rebase, by using the `output` function defined in `git-rebase.sh`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/sequencer.c b/sequencer.c
index dfa4fab98b..edc213a2c8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -464,6 +464,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
o.ancestor = base ? base_label : "(empty tree)";
o.branch1 = "HEAD";
o.branch2 = next ? next_label : "(empty tree)";
+ if (is_rebase_i(opts))
+ o.buffer_output = 2;
head_tree = parse_tree_indirect(head);
next_tree = next ? next->tree : empty_tree();
@@ -475,6 +477,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
clean = merge_trees(&o,
head_tree,
next_tree, base_tree, &result);
+ if (is_rebase_i(opts) && clean <= 0)
+ fputs(o.obuf.buf, stdout);
strbuf_release(&o.obuf);
if (clean < 0)
return clean;
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 11/34] sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed
From: Johannes Schindelin @ 2016-12-13 15:30 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
The scripted version of the interactive rebase already does that.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/sequencer.c b/sequencer.c
index 855d3ba503..abffaf3b40 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1835,8 +1835,13 @@ static int commit_staged_changes(struct replay_opts *opts)
if (has_unstaged_changes(1))
return error(_("cannot rebase: You have unstaged changes."));
- if (!has_uncommitted_changes(0))
+ if (!has_uncommitted_changes(0)) {
+ const char *cherry_pick_head = git_path("CHERRY_PICK_HEAD");
+
+ if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
+ return error(_("could not remove CHERRY_PICK_HEAD"));
return 0;
+ }
if (file_exists(rebase_path_amend())) {
struct strbuf rev = STRBUF_INIT;
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 10/34] sequencer (rebase -i): allow continuing with staged changes
From: Johannes Schindelin @ 2016-12-13 15:30 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
When an interactive rebase is interrupted, the user may stage changes
before continuing, and we need to commit those changes in that case.
Please note that the nested "if" added to the sequencer_continue() is
not combined into a single "if" because it will be extended with an
"else" clause in a later patch in this patch series.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/sequencer.c b/sequencer.c
index 80469b6954..855d3ba503 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1829,6 +1829,42 @@ static int continue_single_pick(void)
return run_command_v_opt(argv, RUN_GIT_CMD);
}
+static int commit_staged_changes(struct replay_opts *opts)
+{
+ int amend = 0;
+
+ if (has_unstaged_changes(1))
+ return error(_("cannot rebase: You have unstaged changes."));
+ if (!has_uncommitted_changes(0))
+ return 0;
+
+ if (file_exists(rebase_path_amend())) {
+ struct strbuf rev = STRBUF_INIT;
+ unsigned char head[20], to_amend[20];
+
+ if (get_sha1("HEAD", head))
+ return error(_("cannot amend non-existing commit"));
+ if (!read_oneliner(&rev, rebase_path_amend(), 0))
+ return error(_("invalid file: '%s'"), rebase_path_amend());
+ if (get_sha1_hex(rev.buf, to_amend))
+ return error(_("invalid contents: '%s'"),
+ rebase_path_amend());
+ if (hashcmp(head, to_amend))
+ return error(_("\nYou have uncommitted changes in your "
+ "working tree. Please, commit them\n"
+ "first and then run 'git rebase "
+ "--continue' again."));
+
+ strbuf_release(&rev);
+ amend = 1;
+ }
+
+ if (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))
+ return error(_("could not commit staged changes."));
+ unlink(rebase_path_amend());
+ return 0;
+}
+
int sequencer_continue(struct replay_opts *opts)
{
struct todo_list todo_list = TODO_LIST_INIT;
@@ -1837,6 +1873,10 @@ int sequencer_continue(struct replay_opts *opts)
if (read_and_refresh_cache(opts))
return -1;
+ if (is_rebase_i(opts)) {
+ if (commit_staged_changes(opts))
+ return -1;
+ }
if (!file_exists(get_todo_path(opts)))
return continue_single_pick();
if (read_populate_opts(opts))
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 09/34] sequencer (rebase -i): write an author-script file
From: Johannes Schindelin @ 2016-12-13 15:30 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
When the interactive rebase aborts, it writes out an author-script file
to record the author information for the current commit. As we are about
to teach the sequencer how to perform the actions behind an interactive
rebase, it needs to write those author-script files, too.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/sequencer.c b/sequencer.c
index e443f4765d..80469b6954 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -515,6 +515,53 @@ static int is_index_unchanged(void)
return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
}
+static int write_author_script(const char *message)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *eol;
+ int res;
+
+ for (;;)
+ if (!*message || starts_with(message, "\n")) {
+missing_author:
+ /* Missing 'author' line? */
+ unlink(rebase_path_author_script());
+ return 0;
+ }
+ else if (skip_prefix(message, "author ", &message))
+ break;
+ else if ((eol = strchr(message, '\n')))
+ message = eol + 1;
+ else
+ goto missing_author;
+
+ strbuf_addstr(&buf, "GIT_AUTHOR_NAME='");
+ while (*message && *message != '\n' && *message != '\r')
+ if (skip_prefix(message, " <", &message))
+ break;
+ else if (*message != '\'')
+ strbuf_addch(&buf, *(message++));
+ else
+ strbuf_addf(&buf, "'\\\\%c'", *(message++));
+ strbuf_addstr(&buf, "'\nGIT_AUTHOR_EMAIL='");
+ while (*message && *message != '\n' && *message != '\r')
+ if (skip_prefix(message, "> ", &message))
+ break;
+ else if (*message != '\'')
+ strbuf_addch(&buf, *(message++));
+ else
+ strbuf_addf(&buf, "'\\\\%c'", *(message++));
+ strbuf_addstr(&buf, "'\nGIT_AUTHOR_DATE='@");
+ while (*message && *message != '\n' && *message != '\r')
+ if (*message != '\'')
+ strbuf_addch(&buf, *(message++));
+ else
+ strbuf_addf(&buf, "'\\\\%c'", *(message++));
+ res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);
+ strbuf_release(&buf);
+ return res;
+}
+
/*
* Read the author-script file into an environment block, ready for use in
* run_command(), that can be free()d afterwards.
@@ -974,7 +1021,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
}
}
- if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
+ if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
+ res = -1;
+ else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
res = do_recursive_merge(base, next, base_label, next_label,
head, &msgbuf, opts);
if (res < 0)
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 08/34] sequencer (rebase -i): implement the short commands
From: Johannes Schindelin @ 2016-12-13 15:30 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
For users' convenience, most rebase commands can be abbreviated, e.g.
'p' instead of 'pick' and 'x' instead of 'exec'. Let's teach the
sequencer to handle those abbreviated commands just fine.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 36 ++++++++++++++++++++++--------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 271c21581d..e443f4765d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -710,20 +710,23 @@ enum todo_command {
TODO_NOOP
};
-static const char *todo_command_strings[] = {
- "pick",
- "revert",
- "edit",
- "fixup",
- "squash",
- "exec",
- "noop"
+static struct {
+ char c;
+ const char *str;
+} todo_command_info[] = {
+ { 'p', "pick" },
+ { 0, "revert" },
+ { 'e', "edit" },
+ { 'f', "fixup" },
+ { 's', "squash" },
+ { 'x', "exec" },
+ { 0, "noop" }
};
static const char *command_to_string(const enum todo_command command)
{
- if ((size_t)command < ARRAY_SIZE(todo_command_strings))
- return todo_command_strings[command];
+ if ((size_t)command < ARRAY_SIZE(todo_command_info))
+ return todo_command_info[command].str;
die("Unknown command: %d", command);
}
@@ -1125,12 +1128,17 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
return 0;
}
- for (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)
- if (skip_prefix(bol, todo_command_strings[i], &bol)) {
+ for (i = 0; i < ARRAY_SIZE(todo_command_info); i++)
+ if (skip_prefix(bol, todo_command_info[i].str, &bol)) {
item->command = i;
break;
}
- if (i >= ARRAY_SIZE(todo_command_strings))
+ else if (bol[1] == ' ' && *bol == todo_command_info[i].c) {
+ bol++;
+ item->command = i;
+ break;
+ }
+ if (i >= ARRAY_SIZE(todo_command_info))
return -1;
if (item->command == TODO_NOOP) {
@@ -1325,7 +1333,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
{
enum todo_command command = opts->action == REPLAY_PICK ?
TODO_PICK : TODO_REVERT;
- const char *command_string = todo_command_strings[command];
+ const char *command_string = todo_command_info[command].str;
struct commit *commit;
if (prepare_revs(opts))
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command
From: Johannes Schindelin @ 2016-12-13 15:29 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
The 'noop' command is probably the most boring of all rebase -i commands
to support in the sequencer.
Which makes it an excellent candidate for this first stab to add support
for rebase -i's commands to the sequencer.
For the moment, let's also treat empty lines and commented-out lines as
'noop'; We will refine that handling later in this patch series.
To make it easier to identify "classes" of todo_commands (such as:
determine whether a command is pick-like, i.e. handles a single commit),
let's enforce a certain order of said commands.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 35 ++++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 21cfdacd06..1224799286 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -639,14 +639,23 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)
return 1;
}
+/*
+ * Note that ordering matters in this enum. Not only must it match the mapping
+ * below, it is also divided into several sections that matter. When adding
+ * new commands, make sure you add it in the right section.
+ */
enum todo_command {
+ /* commands that handle commits */
TODO_PICK = 0,
- TODO_REVERT
+ TODO_REVERT,
+ /* commands that do nothing but are counted for reporting progress */
+ TODO_NOOP
};
static const char *todo_command_strings[] = {
"pick",
- "revert"
+ "revert",
+ "noop"
};
static const char *command_to_string(const enum todo_command command)
@@ -916,6 +925,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
/* left-trim */
bol += strspn(bol, " \t");
+ if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
+ item->command = TODO_NOOP;
+ item->commit = NULL;
+ item->arg = bol;
+ item->arg_len = eol - bol;
+ return 0;
+ }
+
for (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)
if (skip_prefix(bol, todo_command_strings[i], &bol)) {
item->command = i;
@@ -924,6 +941,13 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
if (i >= ARRAY_SIZE(todo_command_strings))
return -1;
+ if (item->command == TODO_NOOP) {
+ item->commit = NULL;
+ item->arg = bol;
+ item->arg_len = eol - bol;
+ return 0;
+ }
+
/* Eat up extra spaces/ tabs before object name */
padding = strspn(bol, " \t");
if (!padding)
@@ -1292,7 +1316,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
struct todo_item *item = todo_list->items + todo_list->current;
if (save_todo(todo_list, opts))
return -1;
- res = do_pick_commit(item->command, item->commit, opts);
+ if (item->command <= TODO_REVERT)
+ res = do_pick_commit(item->command, item->commit,
+ opts);
+ else if (item->command != TODO_NOOP)
+ return error(_("unknown command %d"), item->command);
+
todo_list->current++;
if (res)
return res;
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'
From: Johannes Schindelin @ 2016-12-13 15:29 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
This patch introduces a new action for the sequencer. It really does not
do a whole lot of its own right now, but lays the ground work for
patches to come. The intention, of course, is to finally make the
sequencer the work horse of the interactive rebase (the original idea
behind the "sequencer" concept).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 33 +++++++++++++++++++++++++++++----
sequencer.h | 3 ++-
2 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 30b10ba143..21cfdacd06 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -28,6 +28,14 @@ static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
+static GIT_PATH_FUNC(rebase_path, "rebase-merge")
+/*
+ * The file containing rebase commands, comments, and empty lines.
+ * This file is created by "git rebase -i" then edited by the user. As
+ * the lines are processed, they are removed from the front of this
+ * file and written to the tail of 'done'.
+ */
+static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
/*
* A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
* GIT_AUTHOR_DATE that will be used for the commit that is currently
@@ -40,19 +48,22 @@ static GIT_PATH_FUNC(rebase_path_author_script, "rebase-merge/author-script")
*/
static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
-/* We will introduce the 'interactive rebase' mode later */
static inline int is_rebase_i(const struct replay_opts *opts)
{
- return 0;
+ return opts->action == REPLAY_INTERACTIVE_REBASE;
}
static const char *get_dir(const struct replay_opts *opts)
{
+ if (is_rebase_i(opts))
+ return rebase_path();
return git_path_seq_dir();
}
static const char *get_todo_path(const struct replay_opts *opts)
{
+ if (is_rebase_i(opts))
+ return rebase_path_todo();
return git_path_todo_file();
}
@@ -168,7 +179,15 @@ int sequencer_remove_state(struct replay_opts *opts)
static const char *action_name(const struct replay_opts *opts)
{
- return opts->action == REPLAY_REVERT ? N_("revert") : N_("cherry-pick");
+ switch (opts->action) {
+ case REPLAY_REVERT:
+ return N_("revert");
+ case REPLAY_PICK:
+ return N_("cherry-pick");
+ case REPLAY_INTERACTIVE_REBASE:
+ return N_("rebase -i");
+ }
+ die(_("Unknown action: %d"), opts->action);
}
struct commit_message {
@@ -395,7 +414,10 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
if (active_cache_changed &&
write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
- /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
+ /*
+ * TRANSLATORS: %s will be "revert", "cherry-pick" or
+ * "rebase -i".
+ */
return error(_("%s: Unable to write new index file"),
_(action_name(opts)));
rollback_lock_file(&index_lock);
@@ -1204,6 +1226,9 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
const char *todo_path = get_todo_path(opts);
int next = todo_list->current, offset, fd;
+ if (is_rebase_i(opts))
+ next++;
+
fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
if (fd < 0)
return error_errno(_("could not lock '%s'"), todo_path);
diff --git a/sequencer.h b/sequencer.h
index 7a513c576b..cb21cfddee 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -7,7 +7,8 @@ const char *git_path_seq_dir(void);
enum replay_action {
REPLAY_REVERT,
- REPLAY_PICK
+ REPLAY_PICK,
+ REPLAY_INTERACTIVE_REBASE
};
struct replay_opts {
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 00/34] Teach the sequencer to act as rebase -i's backend
From: Johannes Schindelin @ 2016-12-13 15:29 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1472633606.git.johannes.schindelin@gmx.de>
This marks the count down to '3': two more patch series after this
(really tiny ones) and we have a faster rebase -i.
The idea of this patch series is to teach the sequencer to understand
all of the commands in `git-rebase-todo` scripts, to execute them and to
behave pretty much very the same as `git rebase -i --continue` when
called with the newly-introduced REPLAY_INTERACTIVE_REBASE mode.
Most of these patches should be pretty much straight-forward. When not,
I tried to make a point of describing enough background in the commit
message. Please feel free to point out where my explanations fall short.
Note that even after this patch series is applied, rebase -i is still
unaffected. It will require the next patch series which introduces the
rebase--helper that essentially implements `git rebase -i --continue` by
calling the sequencer with the appropriate options.
The final patch series will move a couple of pre- and post-processing
steps into the rebase--helper/sequencer (such as expanding/shrinking the
SHA-1s, reordering the fixup!/squash! lines, etc). This might sound like
a mere add-on, but it is essential for the speed improvements: those
stupid little processing steps really dominated the execution time in my
tests.
Apart from mostly cosmetic patches (and the occasional odd bug that I
fixed promptly), I used these patches since mid May to perform all of my
interactive rebases. In mid June, I had the idea to teach rebase -i to
run *both* scripted rebase and rebase--helper and to cross-validate the
results. This slowed down all my interactive rebases since, but helped
me catch three rather obscure bugs (e.g. that git commit --fixup unfolds
long onelines and rebase -i still finds the correct original commit).
This is all only to say that I am rather confident that the current code
does the job.
Since sending out v1, I integrated all of these patch series
into Git for Windows v2.10.0, where they have been live ever since, and
used by myself (also in a Linux VM, as Git for Windows' master branch
always builds also on Linux and passes the test suite, too).
Just to reiterate why I do all this: it speeds up the interactive rebase
substantially. Even with a not yet fully builtin rebase -i, but just the
part after the user edited the `git-rebase-todo` script.
The performance test I introduced to demonstrate this (p3404) shows a
speed-up of +380% here (i.e. roughly 5x), from ~8.8 seconds to ~1.8
seconds. This is on Windows, where the performance impact of avoiding
shell scripting is most noticable.
On MacOSX and on Linux, the speed-up is less pronounced, but still
noticable, at least if you trust Travis CI, which I abused to perform
that test for me. Check for yourself (searching for "3404.2") here:
https://travis-ci.org/git/git/builds/156295227. According to those logs,
p3404 is speeded up from ~0.45 seconds to ~0.12 seconds on Linux (read:
about 3.5x) and from ~1.7 seconds to ~0.5 seconds on MacOSX (read:
almost 4x).
Please note that the interdiff vs v1 is only of limited use: too many
things changed in the meantime, in particular the prepare-sequencer
branch that went through a couple of iterations before it found its way
into git.git's master branch. So please take the interdiff with a
mountain range of salt.
Changes since v1:
- some grammar touch-ups.
- simplified determining the command string in
walk_revs_populate_todo().
- removed the beautiful ordinal logic (to print out "1st", "2nd", "3rd"
etc) and made things consistent with the current `rebase -i`.
- while at it, marked more messages for translation.
- added code-comments to clarify the order, and the sections, of the
todo_command enum.
- replaced one error(..., strerror(...)) to an error_errno(...).
- downcased error messages
- marked error messages for translation
- adjusted the patches to account for sequencer_entrust() having been
removed from the prepare-sequencer patch series by request of Junio.
- moved the introduction of write_message_gently() into the patch
introducing its first usage, i.e. the support for the 'edit' command.
- adjusted some indentations
- prevented an write_in_full() from being called after a failed open()
- inserted a few forgotten strbuf_release() calls
Johannes Schindelin (34):
sequencer: support a new action: 'interactive rebase'
sequencer (rebase -i): implement the 'noop' command
sequencer (rebase -i): implement the 'edit' command
sequencer (rebase -i): implement the 'exec' command
sequencer (rebase -i): learn about the 'verbose' mode
sequencer (rebase -i): write the 'done' file
sequencer (rebase -i): add support for the 'fixup' and 'squash'
commands
sequencer (rebase -i): implement the short commands
sequencer (rebase -i): write an author-script file
sequencer (rebase -i): allow continuing with staged changes
sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed
sequencer (rebase -i): skip some revert/cherry-pick specific code path
sequencer (rebase -i): the todo can be empty when continuing
sequencer (rebase -i): update refs after a successful rebase
sequencer (rebase -i): leave a patch upon error
sequencer (rebase -i): implement the 'reword' command
sequencer (rebase -i): allow fast-forwarding for edit/reword
sequencer (rebase -i): refactor setting the reflog message
sequencer (rebase -i): set the reflog message consistently
sequencer (rebase -i): copy commit notes at end
sequencer (rebase -i): record interrupted commits in rewritten, too
sequencer (rebase -i): run the post-rewrite hook, if needed
sequencer (rebase -i): respect the rebase.autostash setting
sequencer (rebase -i): respect strategy/strategy_opts settings
sequencer (rebase -i): allow rescheduling commands
sequencer (rebase -i): implement the 'drop' command
sequencer (rebase -i): differentiate between comments and 'noop'
run_command_opt(): optionally hide stderr when the command succeeds
sequencer (rebase -i): show only failed `git commit`'s output
sequencer (rebase -i): show only failed cherry-picks' output
sequencer (rebase -i): suggest --edit-todo upon unknown command
sequencer (rebase -i): show the progress
sequencer (rebase -i): write the progress into files
sequencer (rebase -i): write out the final message
run-command.c | 23 ++
run-command.h | 1 +
sequencer.c | 1003 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
sequencer.h | 4 +-
4 files changed, 983 insertions(+), 48 deletions(-)
base-commit: 8d7a455ed52e2a96debc080dfc011b6bb00db5d2
Published-As: https://github.com/dscho/git/releases/tag/sequencer-i-v2
Fetch-It-Via: git fetch https://github.com/dscho/git sequencer-i-v2
Interdiff vs v1:
diff --git a/sequencer.c b/sequencer.c
index 6ca9d1e09d..41be4cde16 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -125,7 +125,6 @@ static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash")
static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
-/* We will introduce the 'interactive rebase' mode later */
static inline int is_rebase_i(const struct replay_opts *opts)
{
return opts->action == REPLAY_INTERACTIVE_REBASE;
@@ -259,13 +258,13 @@ static const char *action_name(const struct replay_opts *opts)
{
switch (opts->action) {
case REPLAY_REVERT:
- return "revert";
+ return N_("revert");
case REPLAY_PICK:
- return "cherry-pick";
+ return N_("cherry-pick");
case REPLAY_INTERACTIVE_REBASE:
- return "rebase -i";
+ return N_("rebase -i");
}
- die("Unknown action: %d", opts->action);
+ die(_("Unknown action: %d"), opts->action);
}
struct commit_message {
@@ -548,6 +547,7 @@ static int write_author_script(const char *message)
{
struct strbuf buf = STRBUF_INIT;
const char *eol;
+ int res;
for (;;)
if (!*message || starts_with(message, "\n")) {
@@ -585,8 +585,9 @@ static int write_author_script(const char *message)
strbuf_addch(&buf, *(message++));
else
strbuf_addf(&buf, "'\\\\%c'", *(message++));
- strbuf_addstr(&buf, "'\n");
- return write_message(&buf, rebase_path_author_script());
+ res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);
+ strbuf_release(&buf);
+ return res;
}
/*
@@ -771,16 +772,25 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)
return 1;
}
+/*
+ * Note that ordering matters in this enum. Not only must it match the mapping
+ * below, it is also divided into several sections that matter. When adding
+ * new commands, make sure you add it in the right section.
+ */
enum todo_command {
+ /* commands that handle commits */
TODO_PICK = 0,
TODO_REVERT,
TODO_EDIT,
TODO_REWORD,
TODO_FIXUP,
TODO_SQUASH,
+ /* commands that do something else than handling a single commit */
TODO_EXEC,
+ /* commands that do nothing but are counted for reporting progress */
TODO_NOOP,
TODO_DROP,
+ /* comments (not counted for reporting progress) */
TODO_COMMENT
};
@@ -812,47 +822,34 @@ static int is_fixup(enum todo_command command)
return command == TODO_FIXUP || command == TODO_SQUASH;
}
-static const char *nth_for_number(int n)
-{
- int n1 = n % 10, n10 = n % 100;
-
- if (n1 == 1 && n10 != 11)
- return "st";
- if (n1 == 2 && n10 != 12)
- return "nd";
- if (n1 == 3 && n10 != 13)
- return "rd";
- return "th";
-}
-
static int update_squash_messages(enum todo_command command,
struct commit *commit, struct replay_opts *opts)
{
struct strbuf buf = STRBUF_INIT;
- int count;
+ int count, res;
const char *message, *body;
if (file_exists(rebase_path_squash_msg())) {
char *p, *p2;
if (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)
- return error("Could not read %s",
+ return error(_("could not read '%s'"),
rebase_path_squash_msg());
- if (buf.buf[0] == '\n' || !skip_prefix(buf.buf + 1,
- " This is a combination of ",
- (const char **)&p))
- return error("Unexpected 1st line of squash message:\n"
- "\n\t%.*s",
- (int)(strchrnul(buf.buf, '\n') - buf.buf),
- buf.buf);
+ if (buf.buf[0] != comment_line_char ||
+ !skip_prefix(buf.buf + 1, " This is a combination of ",
+ (const char **)&p))
+ return error(_("unexpected 1st line of squash message:"
+ "\n\n\t%.*s"),
+ (int)(strchrnul(buf.buf, '\n') - buf.buf),
+ buf.buf);
count = strtol(p, &p2, 10);
if (count < 1 || *p2 != ' ')
- return error("Invalid 1st line of squash message:\n"
- "\n\t%.*s",
- (int)(strchrnul(buf.buf, '\n') - buf.buf),
- buf.buf);
+ return error(_("invalid 1st line of squash message:\n"
+ "\n\t%.*s"),
+ (int)(strchrnul(buf.buf, '\n') - buf.buf),
+ buf.buf);
sprintf((char *)p, "%d", ++count);
if (!*p2)
@@ -868,32 +865,33 @@ static int update_squash_messages(enum todo_command command,
const char *head_message, *body;
if (get_sha1("HEAD", head))
- return error("Need a HEAD to fixup");
+ return error(_("need a HEAD to fixup"));
if (!(head_commit = lookup_commit_reference(head)))
- return error("Could not read HEAD");
+ return error(_("could not read HEAD"));
if (!(head_message = get_commit_buffer(head_commit, NULL)))
- return error("Could not read HEAD's commit message");
+ return error(_("could not read HEAD's commit message"));
body = strstr(head_message, "\n\n");
if (!body)
body = "";
else
body = skip_blank_lines(body + 2);
- if (write_file_gently(rebase_path_fixup_msg(), body, 0))
- return error("Cannot write %s",
- rebase_path_fixup_msg());
+ if (write_message(body, strlen(body),
+ rebase_path_fixup_msg(), 0))
+ return error(_("cannot write '%s'"),
+ rebase_path_fixup_msg());
count = 2;
- strbuf_addf(&buf, "%c This is a combination of 2 commits.\n"
- "%c The first commit's message is:\n\n%s",
- comment_line_char, comment_line_char, body);
+ strbuf_addf(&buf, _("%c This is a combination of 2 commits.\n"
+ "%c The first commit's message is:\n\n%s"),
+ comment_line_char, comment_line_char, body);
unuse_commit_buffer(head_commit, head_message);
}
if (!(message = get_commit_buffer(commit, NULL)))
- return error("Could not read commit message of %s",
- oid_to_hex(&commit->object.oid));
+ return error(_("could not read commit message of %s"),
+ oid_to_hex(&commit->object.oid));
body = strstr(message, "\n\n");
if (!body)
body = "";
@@ -902,21 +900,23 @@ static int update_squash_messages(enum todo_command command,
if (command == TODO_SQUASH) {
unlink(rebase_path_fixup_msg());
- strbuf_addf(&buf, "\n%c This is the %d%s commit message:\n\n%s",
- comment_line_char,
- count, nth_for_number(count), body);
+ strbuf_addf(&buf, _("\n%c This is the commit message #%d:\n"
+ "\n%s"),
+ comment_line_char, count, body);
}
else if (command == TODO_FIXUP) {
- strbuf_addf(&buf,
- "\n%c The %d%s commit message will be skipped:\n\n",
- comment_line_char, count, nth_for_number(count));
+ strbuf_addf(&buf, _("\n%c The commit message #%d "
+ "will be skipped:\n\n"),
+ comment_line_char, count);
strbuf_add_commented_lines(&buf, body, strlen(body));
}
else
- return error("Unknown command: %d", command);
+ return error(_("unknown command: %d"), command);
unuse_commit_buffer(commit, message);
- return write_message(&buf, rebase_path_squash_msg());
+ res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
+ strbuf_release(&buf);
+ return res;
}
static void flush_rewritten_pending(void) {
@@ -940,6 +940,7 @@ static void flush_rewritten_pending(void) {
fclose(out);
unlink(rebase_path_rewritten_pending());
}
+ strbuf_release(&buf);
}
static void record_in_rewritten(struct object_id *oid,
@@ -1013,7 +1014,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
parent = commit->parents->item;
if (get_message(commit, &msg) != 0)
- return error(_("Cannot get commit message for %s"),
+ return error(_("cannot get commit message for %s"),
oid_to_hex(&commit->object.oid));
if (opts->allow_ff && !is_fixup(command) &&
@@ -1021,7 +1022,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
(!parent && unborn))) {
if (is_rebase_i(opts))
write_author_script(msg.message);
- res |= fast_forward_to(commit->object.oid.hash, head, unborn,
+ res = fast_forward_to(commit->object.oid.hash, head, unborn,
opts);
if (res || command != TODO_REWORD)
goto leave;
@@ -1100,8 +1101,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
const char *dest = git_path("SQUASH_MSG");
unlink(dest);
if (copy_file(dest, rebase_path_squash_msg(), 0666))
- return error("Could not rename %s to "
- "%s", rebase_path_squash_msg(), dest);
+ return error(_("could not rename '%s' to '%s'"),
+ rebase_path_squash_msg(), dest);
unlink(git_path("MERGE_MSG"));
msg_file = dest;
edit = 1;
@@ -1109,7 +1110,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
}
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
- res |= -1;
+ res = -1;
else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
res = do_recursive_merge(base, next, base_label, next_label,
head, &msgbuf, opts);
@@ -1166,8 +1167,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
}
if (!opts->no_commit)
fast_forward_edit:
- res |= sequencer_commit(msg_file, opts, allow, edit, amend,
- cleanup_commit_message);
+ res = run_git_commit(msg_file, opts, allow, edit, amend,
+ cleanup_commit_message);
if (!res && final_fixup) {
unlink(rebase_path_fixup_msg());
@@ -1317,7 +1318,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
{
struct todo_item *item;
- char *p = buf;
+ char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
for (i = 1; *p; i++, p = next_p) {
@@ -1335,15 +1336,16 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
i, (int)(eol - p), p);
item->command = TODO_NOOP;
}
+
if (fixup_okay)
; /* do nothing */
else if (is_fixup(item->command))
- return error("Cannot '%s' without a previous commit",
+ return error(_("cannot '%s' without a previous commit"),
command_to_string(item->command));
else if (item->command < TODO_NOOP)
fixup_okay = 1;
- p = *eol ? eol + 1 : eol;
}
+
return res;
}
@@ -1377,13 +1379,14 @@ static int read_populate_todo(struct todo_list *todo_list,
res = parse_insn_buffer(todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
- return error("Please fix this using "
- "'git rebase --edit-todo'.");
- return error(_("Unusable instruction sheet: %s"), todo_file);
+ return error(_("please fix this using "
+ "'git rebase --edit-todo'."));
+ return error(_("unusable instruction sheet: '%s'"), todo_file);
}
+
if (!todo_list->nr &&
(!is_rebase_i(opts) || !file_exists(rebase_path_done())))
- return error(_("No commits parsed."));
+ return error(_("no commits parsed."));
if (!is_rebase_i(opts)) {
enum todo_command valid =
@@ -1408,10 +1411,10 @@ static int read_populate_todo(struct todo_list *todo_list,
todo_list->done_nr = count_commands(&done);
else
todo_list->done_nr = 0;
- todo_list_release(&done);
todo_list->total_nr = todo_list->done_nr
+ count_commands(todo_list);
+ todo_list_release(&done);
if (f) {
fprintf(f, "%d\n", todo_list->total_nr);
@@ -1467,6 +1470,26 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
return 0;
}
+static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
+{
+ int i;
+
+ strbuf_reset(buf);
+ if (!read_oneliner(buf, rebase_path_strategy(), 0))
+ return;
+ opts->strategy = strbuf_detach(buf, NULL);
+ if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
+ return;
+
+ opts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);
+ for (i = 0; i < opts->xopts_nr; i++) {
+ const char *arg = opts->xopts[i];
+
+ skip_prefix(arg, "--", &arg);
+ opts->xopts[i] = xstrdup(arg);
+ }
+}
+
static int read_populate_opts(struct replay_opts *opts)
{
if (is_rebase_i(opts)) {
@@ -1480,30 +1503,12 @@ static int read_populate_opts(struct replay_opts *opts)
opts->gpg_sign = xstrdup(buf.buf + 2);
}
}
- strbuf_release(&buf);
if (file_exists(rebase_path_verbose()))
opts->verbose = 1;
- if (read_oneliner(&buf, rebase_path_strategy(), 0)) {
- opts->strategy =
- sequencer_entrust(opts,
- strbuf_detach(&buf, NULL));
- if (read_oneliner(&buf,
- rebase_path_strategy_opts(), 0)) {
- int i;
- opts->xopts_nr = split_cmdline(buf.buf,
- &opts->xopts);
- for (i = 0; i < opts->xopts_nr; i++)
- skip_prefix(opts->xopts[i], "--",
- &opts->xopts[i]);
- if (opts->xopts_nr)
- sequencer_entrust(opts,
- strbuf_detach(&buf, NULL));
- else
- strbuf_release(&buf);
- }
- }
+ read_strategy_opts(opts, &buf);
+ strbuf_release(&buf);
return 0;
}
@@ -1527,7 +1532,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
{
enum todo_command command = opts->action == REPLAY_PICK ?
TODO_PICK : TODO_REVERT;
- const char *command_string = todo_command_strings[command];
+ const char *command_string = todo_command_info[command].str;
struct commit *commit;
if (prepare_revs(opts))
@@ -1681,12 +1686,15 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
int prev_offset = !next ? 0 :
todo_list->items[next - 1].offset_in_buf;
- if (offset > prev_offset && write_in_full(fd,
- todo_list->buf.buf + prev_offset,
- offset - prev_offset) < 0)
- return error(_("Could not write to %s (%s)"),
- done_path, strerror(errno));
- close(fd);
+ if (fd >= 0 && offset > prev_offset &&
+ write_in_full(fd, todo_list->buf.buf + prev_offset,
+ offset - prev_offset) < 0) {
+ close(fd);
+ return error_errno(_("could not write to '%s'"),
+ done_path);
+ }
+ if (fd >= 0)
+ close(fd);
}
return 0;
}
@@ -1730,11 +1738,11 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
{
struct strbuf buf = STRBUF_INIT;
struct rev_info log_tree_opt;
- const char *commit_buffer = get_commit_buffer(commit, NULL), *subject;
+ const char *commit_buffer = get_commit_buffer(commit, NULL), *subject, *p;
int res = 0;
- if (write_file_gently(rebase_path_stopped_sha(),
- short_commit_name(commit), 1) < 0)
+ p = short_commit_name(commit);
+ if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
return -1;
strbuf_addf(&buf, "%s/patch", get_dir(opts));
@@ -1748,7 +1756,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
log_tree_opt.diffopt.file = fopen(buf.buf, "w");
log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;
if (!log_tree_opt.diffopt.file)
- res |= error_errno("could not open '%s'", buf.buf);
+ res |= error_errno(_("could not open '%s'"), buf.buf);
else {
res |= log_tree_commit(&log_tree_opt, commit);
fclose(log_tree_opt.diffopt.file);
@@ -1758,7 +1766,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
strbuf_addf(&buf, "%s/message", get_dir(opts));
if (!file_exists(buf.buf)) {
find_commit_subject(commit_buffer, &subject);
- res |= write_file_gently(buf.buf, subject, 1);
+ res |= write_message(subject, strlen(subject), buf.buf, 1);
unuse_commit_buffer(commit, commit_buffer);
}
strbuf_release(&buf);
@@ -1769,11 +1777,13 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
static int intend_to_amend(void)
{
unsigned char head[20];
+ char *p;
if (get_sha1("HEAD", head))
- return error("Cannot read HEAD");
+ return error(_("cannot read HEAD"));
- return write_file_gently(rebase_path_amend(), sha1_to_hex(head), 1);
+ p = sha1_to_hex(head);
+ return write_message(p, strlen(p), rebase_path_amend(), 1);
}
static int error_with_patch(struct commit *commit,
@@ -1806,13 +1816,13 @@ static int error_failed_squash(struct commit *commit,
struct replay_opts *opts, int subject_len, const char *subject)
{
if (rename(rebase_path_squash_msg(), rebase_path_message()))
- return error("Could not rename %s to %s",
+ return error(_("could not rename '%s' to '%s'"),
rebase_path_squash_msg(), rebase_path_message());
unlink(rebase_path_fixup_msg());
unlink(git_path("MERGE_MSG"));
if (copy_file(git_path("MERGE_MSG"), rebase_path_message(), 0666))
- return error("Could not copy %s to %s", rebase_path_message(),
- git_path("MERGE_MSG"));
+ return error(_("could not copy '%s' to '%s'"),
+ rebase_path_message(), git_path("MERGE_MSG"));
return error_with_patch(commit, subject, subject_len, opts, 1, 0);
}
@@ -1827,30 +1837,30 @@ static int do_exec(const char *command_line)
/* force re-reading of the cache */
if (discard_cache() < 0 || read_cache() < 0)
- return error(_("Could not read index"));
+ return error(_("could not read index"));
dirty = require_clean_work_tree("rebase", NULL, 1, 1);
if (status) {
- warning("Execution failed: %s\n%s"
- "You can fix the problem, and then run\n"
- "\n"
- " git rebase --continue\n"
- "\n",
+ warning(_("execution failed: %s\n%s"
+ "You can fix the problem, and then run\n"
+ "\n"
+ " git rebase --continue\n"
+ "\n"),
command_line,
- dirty ? "and made changes to the index and/or the "
- "working tree\n" : "");
+ dirty ? N_("and made changes to the index and/or the "
+ "working tree\n") : "");
if (status == 127)
/* command not found */
status = 1;
}
else if (dirty) {
- warning("Execution succeeded: %s\nbut "
- "left changes to the index and/or the working tree\n"
- "Commit or stash your changes, and then run\n"
- "\n"
- " git rebase --continue\n"
- "\n", command_line);
+ warning(_("execution succeeded: %s\nbut "
+ "left changes to the index and/or the working tree\n"
+ "Commit or stash your changes, and then run\n"
+ "\n"
+ " git rebase --continue\n"
+ "\n"), command_line);
status = 1;
}
@@ -1912,7 +1922,7 @@ static int apply_autostash(struct replay_opts *opts)
argv_array_push(&store.args, "-q");
argv_array_push(&store.args, stash_sha1.buf);
if (run_command(&store))
- ret = error(_("Cannot store %s"), stash_sha1.buf);
+ ret = error(_("cannot store %s"), stash_sha1.buf);
else
printf(_("Applying autostash resulted in conflicts.\n"
"Your changes are safe in the stash.\n"
@@ -1995,7 +2005,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
if (item->command == TODO_EDIT) {
struct commit *commit = item->commit;
if (!res)
- warning("Stopped at %s... %.*s",
+ warning(_("stopped at %s... %.*s"),
short_commit_name(commit),
item->arg_len, item->arg);
return error_with_patch(commit,
@@ -2025,7 +2035,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
*end_of_arg = saved;
}
else if (item->command < TODO_NOOP)
- return error("Unknown command %d", item->command);
+ return error(_("unknown command %d"), item->command);
todo_list->current++;
if (res)
@@ -2046,22 +2056,22 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
unsigned char head[20], orig[20];
if (get_sha1("HEAD", head))
- return error("Cannot read HEAD");
+ return error(_("cannot read HEAD"));
if (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||
get_sha1_hex(buf.buf, orig))
- return error("Could not read orig-head");
+ return error(_("could not read orig-head"));
if (!read_oneliner(&buf, rebase_path_onto(), 0))
- return error("Could not read 'onto'");
+ return error(_("could not read 'onto'"));
msg = reflog_message(opts, "finish", "%s onto %s",
head_ref.buf, buf.buf);
if (update_ref(msg, head_ref.buf, head, orig,
REF_NODEREF, UPDATE_REFS_MSG_ON_ERR))
- return error("Could not update %s",
+ return error(_("could not update %s"),
head_ref.buf);
msg = reflog_message(opts, "finish", "returning to %s",
head_ref.buf);
if (create_symref("HEAD", head_ref.buf, msg))
- return error("Could not update HEAD to %s",
+ return error(_("could not update HEAD to %s"),
head_ref.buf);
strbuf_reset(&buf);
}
@@ -2072,7 +2082,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
};
if (!read_oneliner(&buf, rebase_path_orig_head(), 0))
- return error("Could not read %s",
+ return error(_("could not read '%s'"),
rebase_path_orig_head());
strbuf_addstr(&buf, "..HEAD");
argv[2] = buf.buf;
@@ -2099,6 +2109,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
hook.in = open(rebase_path_rewritten_list(),
O_RDONLY);
+ hook.stdout_to_stderr = 1;
argv_array_push(&hook.args, post_rewrite_hook);
argv_array_push(&hook.args, "rebase");
/* we don't care if this hook failed */
@@ -2136,12 +2147,12 @@ static int commit_staged_changes(struct replay_opts *opts)
int amend = 0;
if (has_unstaged_changes(1))
- return error(_("Cannot rebase: You have unstaged changes."));
+ return error(_("cannot rebase: You have unstaged changes."));
if (!has_uncommitted_changes(0)) {
const char *cherry_pick_head = git_path("CHERRY_PICK_HEAD");
if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
- return error("Could not remove CHERRY_PICK_HEAD");
+ return error(_("could not remove CHERRY_PICK_HEAD"));
return 0;
}
@@ -2150,23 +2161,24 @@ static int commit_staged_changes(struct replay_opts *opts)
unsigned char head[20], to_amend[20];
if (get_sha1("HEAD", head))
- return error("Cannot amend non-existing commit");
+ return error(_("cannot amend non-existing commit"));
if (!read_oneliner(&rev, rebase_path_amend(), 0))
- return error("Invalid file: %s", rebase_path_amend());
+ return error(_("invalid file: '%s'"), rebase_path_amend());
if (get_sha1_hex(rev.buf, to_amend))
- return error("Invalid contents: %s",
+ return error(_("invalid contents: '%s'"),
rebase_path_amend());
if (hashcmp(head, to_amend))
- return error("\nYou have uncommitted changes in your "
- "working tree. Please, commit them\nfirst and "
- "then run 'git rebase --continue' again.");
+ return error(_("\nYou have uncommitted changes in your "
+ "working tree. Please, commit them\n"
+ "first and then run 'git rebase "
+ "--continue' again."));
strbuf_release(&rev);
amend = 1;
}
- if (sequencer_commit(rebase_path_message(), opts, 1, 1, amend, 0))
- return error("Could not commit staged changes.");
+ if (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))
+ return error(_("could not commit staged changes."));
unlink(rebase_path_amend());
return 0;
}
@@ -2194,23 +2206,23 @@ int sequencer_continue(struct replay_opts *opts)
/* Verify that the conflict has been resolved */
if (file_exists(git_path_cherry_pick_head()) ||
file_exists(git_path_revert_head())) {
- int ret = continue_single_pick();
- if (ret)
- return ret;
+ res = continue_single_pick();
+ if (res)
+ goto release_todo_list;
+ }
+ if (index_differs_from("HEAD", 0, 0)) {
+ res = error_dirty_index(opts);
+ goto release_todo_list;
}
- if (index_differs_from("HEAD", 0))
- return error_dirty_index(opts);
todo_list.current++;
}
else if (file_exists(rebase_path_stopped_sha())) {
struct strbuf buf = STRBUF_INIT;
struct object_id oid;
- if (read_oneliner(&buf, rebase_path_stopped_sha(), 1)) {
- if (!get_sha1_committish(buf.buf, oid.hash))
- record_in_rewritten(&oid,
- peek_command(&todo_list, 0));
- }
+ if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
+ !get_sha1_committish(buf.buf, oid.hash))
+ record_in_rewritten(&oid, peek_command(&todo_list, 0));
strbuf_release(&buf);
}
--
2.11.0.rc3.windows.1
^ permalink raw reply
* [PATCH v2 31/34] sequencer (rebase -i): suggest --edit-todo upon unknown command
From: Johannes Schindelin @ 2016-12-13 15:32 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
This is the same behavior as known from `git rebase -i`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/sequencer.c b/sequencer.c
index edc213a2c8..498dd028d1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1355,8 +1355,12 @@ static int read_populate_todo(struct todo_list *todo_list,
close(fd);
res = parse_insn_buffer(todo_list->buf.buf, todo_list);
- if (res)
+ if (res) {
+ if (is_rebase_i(opts))
+ return error(_("please fix this using "
+ "'git rebase --edit-todo'."));
return error(_("unusable instruction sheet: '%s'"), todo_file);
+ }
if (!todo_list->nr &&
(!is_rebase_i(opts) || !file_exists(rebase_path_done())))
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 34/34] sequencer (rebase -i): write out the final message
From: Johannes Schindelin @ 2016-12-13 15:32 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
The shell script version of the interactive rebase has a very specific
final message. Teach the sequencer to print the same.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/sequencer.c b/sequencer.c
index cb5e7f35fc..41be4cde16 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2118,6 +2118,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
}
apply_autostash(opts);
+ fprintf(stderr, "Successfully rebased and updated %s.\n",
+ head_ref.buf);
+
strbuf_release(&buf);
strbuf_release(&head_ref);
}
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 33/34] sequencer (rebase -i): write the progress into files
From: Johannes Schindelin @ 2016-12-13 15:32 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
For the benefit of e.g. the shell prompt, the interactive rebase not
only displays the progress for the user to see, but also writes it into
the msgnum/end files in the state directory.
Teach the sequencer this new trick.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 35d5ef4ef6..cb5e7f35fc 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -45,6 +45,16 @@ static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
*/
static GIT_PATH_FUNC(rebase_path_done, "rebase-merge/done")
/*
+ * The file to keep track of how many commands were already processed (e.g.
+ * for the prompt).
+ */
+static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum");
+/*
+ * The file to keep track of how many commands are to be processed in total
+ * (e.g. for the prompt).
+ */
+static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end");
+/*
* The commit message that is planned to be used for any changes that
* need to be committed following a user interaction.
*/
@@ -1394,6 +1404,7 @@ static int read_populate_todo(struct todo_list *todo_list,
if (is_rebase_i(opts)) {
struct todo_list done = TODO_LIST_INIT;
+ FILE *f = fopen(rebase_path_msgtotal(), "w");
if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
!parse_insn_buffer(done.buf.buf, &done))
@@ -1403,8 +1414,12 @@ static int read_populate_todo(struct todo_list *todo_list,
todo_list->total_nr = todo_list->done_nr
+ count_commands(todo_list);
-
todo_list_release(&done);
+
+ if (f) {
+ fprintf(f, "%d\n", todo_list->total_nr);
+ fclose(f);
+ }
}
return 0;
@@ -1955,11 +1970,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
if (save_todo(todo_list, opts))
return -1;
if (is_rebase_i(opts)) {
- if (item->command != TODO_COMMENT)
+ if (item->command != TODO_COMMENT) {
+ FILE *f = fopen(rebase_path_msgnum(), "w");
+
+ todo_list->done_nr++;
+
+ if (f) {
+ fprintf(f, "%d\n", todo_list->done_nr);
+ fclose(f);
+ }
fprintf(stderr, "Rebasing (%d/%d)%s",
- ++(todo_list->done_nr),
+ todo_list->done_nr,
todo_list->total_nr,
opts->verbose ? "\n" : "\r");
+ }
unlink(rebase_path_message());
unlink(rebase_path_author_script());
unlink(rebase_path_stopped_sha());
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 32/34] sequencer (rebase -i): show the progress
From: Johannes Schindelin @ 2016-12-13 15:32 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
The interactive rebase keeps the user informed about its progress.
If the sequencer wants to do the grunt work of the interactive
rebase, it also needs to show that progress.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/sequencer.c b/sequencer.c
index 498dd028d1..35d5ef4ef6 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1221,6 +1221,7 @@ struct todo_list {
struct strbuf buf;
struct todo_item *items;
int nr, alloc, current;
+ int done_nr, total_nr;
};
#define TODO_LIST_INIT { STRBUF_INIT }
@@ -1338,6 +1339,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
return res;
}
+static int count_commands(struct todo_list *todo_list)
+{
+ int count = 0, i;
+
+ for (i = 0; i < todo_list->nr; i++)
+ if (todo_list->items[i].command != TODO_COMMENT)
+ count++;
+
+ return count;
+}
+
static int read_populate_todo(struct todo_list *todo_list,
struct replay_opts *opts)
{
@@ -1380,6 +1392,21 @@ static int read_populate_todo(struct todo_list *todo_list,
return error(_("cannot revert during a cherry-pick."));
}
+ if (is_rebase_i(opts)) {
+ struct todo_list done = TODO_LIST_INIT;
+
+ if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
+ !parse_insn_buffer(done.buf.buf, &done))
+ todo_list->done_nr = count_commands(&done);
+ else
+ todo_list->done_nr = 0;
+
+ todo_list->total_nr = todo_list->done_nr
+ + count_commands(todo_list);
+
+ todo_list_release(&done);
+ }
+
return 0;
}
@@ -1928,6 +1955,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
if (save_todo(todo_list, opts))
return -1;
if (is_rebase_i(opts)) {
+ if (item->command != TODO_COMMENT)
+ fprintf(stderr, "Rebasing (%d/%d)%s",
+ ++(todo_list->done_nr),
+ todo_list->total_nr,
+ opts->verbose ? "\n" : "\r");
unlink(rebase_path_message());
unlink(rebase_path_author_script());
unlink(rebase_path_stopped_sha());
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 16/34] sequencer (rebase -i): implement the 'reword' command
From: Johannes Schindelin @ 2016-12-13 15:31 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
This is now trivial, as all the building blocks are in place: all we need
to do is to flip the "edit" switch when committing.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 4361fe0e94..5a9972fec3 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -751,6 +751,7 @@ enum todo_command {
TODO_PICK = 0,
TODO_REVERT,
TODO_EDIT,
+ TODO_REWORD,
TODO_FIXUP,
TODO_SQUASH,
/* commands that do something else than handling a single commit */
@@ -766,6 +767,7 @@ static struct {
{ 'p', "pick" },
{ 0, "revert" },
{ 'e', "edit" },
+ { 'r', "reword" },
{ 'f', "fixup" },
{ 's', "squash" },
{ 'x', "exec" },
@@ -1001,7 +1003,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
}
}
- if (is_fixup(command)) {
+ if (command == TODO_REWORD)
+ edit = 1;
+ else if (is_fixup(command)) {
if (update_squash_messages(command, commit, opts))
return -1;
amend = 1;
@@ -1779,7 +1783,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
}
else if (res && is_rebase_i(opts))
return res | error_with_patch(item->commit,
- item->arg, item->arg_len, opts, res, 0);
+ item->arg, item->arg_len, opts, res,
+ item->command == TODO_REWORD);
}
else if (item->command == TODO_EXEC) {
char *end_of_arg = (char *)(item->arg + item->arg_len);
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 17/34] sequencer (rebase -i): allow fast-forwarding for edit/reword
From: Johannes Schindelin @ 2016-12-13 15:31 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
The sequencer already knew how to fast-forward instead of
cherry-picking, if possible.
We want to continue to do this, of course, but in case of the 'reword'
command, we will need to call `git commit` after fast-forwarding.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 5a9972fec3..33fb36dcbe 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -893,7 +893,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
const char *base_label, *next_label;
struct commit_message msg = { NULL, NULL, NULL, NULL };
struct strbuf msgbuf = STRBUF_INIT;
- int res, unborn = 0, amend = 0, allow;
+ int res, unborn = 0, amend = 0, allow = 0;
if (opts->no_commit) {
/*
@@ -939,11 +939,23 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
else
parent = commit->parents->item;
+ if (get_message(commit, &msg) != 0)
+ return error(_("cannot get commit message for %s"),
+ oid_to_hex(&commit->object.oid));
+
if (opts->allow_ff && !is_fixup(command) &&
((parent && !hashcmp(parent->object.oid.hash, head)) ||
- (!parent && unborn)))
- return fast_forward_to(commit->object.oid.hash, head, unborn, opts);
-
+ (!parent && unborn))) {
+ if (is_rebase_i(opts))
+ write_author_script(msg.message);
+ res = fast_forward_to(commit->object.oid.hash, head, unborn,
+ opts);
+ if (res || command != TODO_REWORD)
+ goto leave;
+ edit = amend = 1;
+ msg_file = NULL;
+ goto fast_forward_edit;
+ }
if (parent && parse_commit(parent) < 0)
/* TRANSLATORS: The first %s will be a "todo" command like
"revert" or "pick", the second %s a SHA1. */
@@ -951,10 +963,6 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
command_to_string(command),
oid_to_hex(&parent->object.oid));
- if (get_message(commit, &msg) != 0)
- return error(_("cannot get commit message for %s"),
- oid_to_hex(&commit->object.oid));
-
/*
* "commit" is an existing commit. We would want to apply
* the difference it introduces since its first parent "prev"
@@ -1084,6 +1092,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
goto leave;
}
if (!opts->no_commit)
+fast_forward_edit:
res = run_git_commit(msg_file, opts, allow, edit, amend,
cleanup_commit_message);
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 19/34] sequencer (rebase -i): set the reflog message consistently
From: Johannes Schindelin @ 2016-12-13 15:31 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
We already used the same reflog message as the scripted version of rebase
-i when finishing. With this commit, we do that also for all the commands
before that.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/sequencer.c b/sequencer.c
index d20efad562..118696f6d3 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1792,6 +1792,10 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
unlink(rebase_path_amend());
}
if (item->command <= TODO_SQUASH) {
+ if (is_rebase_i(opts))
+ setenv("GIT_REFLOG_ACTION", reflog_message(opts,
+ command_to_string(item->command), NULL),
+ 1);
res = do_pick_commit(item->command, item->commit,
opts, is_final_fixup(todo_list));
if (item->command == TODO_EDIT) {
--
2.11.0.rc3.windows.1
^ permalink raw reply related
* [PATCH v2 22/34] sequencer (rebase -i): run the post-rewrite hook, if needed
From: Johannes Schindelin @ 2016-12-13 15:31 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kevin Daudt, Dennis Kaarsemaker
In-Reply-To: <cover.1481642927.git.johannes.schindelin@gmx.de>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/sequencer.c b/sequencer.c
index 0233999389..cafd945e83 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1947,6 +1947,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
if (!stat(rebase_path_rewritten_list(), &st) &&
st.st_size > 0) {
struct child_process child = CHILD_PROCESS_INIT;
+ const char *post_rewrite_hook =
+ find_hook("post-rewrite");
child.in = open(rebase_path_rewritten_list(), O_RDONLY);
child.git_cmd = 1;
@@ -1955,6 +1957,18 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
argv_array_push(&child.args, "--for-rewrite=rebase");
/* we don't care if this copying failed */
run_command(&child);
+
+ if (post_rewrite_hook) {
+ struct child_process hook = CHILD_PROCESS_INIT;
+
+ hook.in = open(rebase_path_rewritten_list(),
+ O_RDONLY);
+ hook.stdout_to_stderr = 1;
+ argv_array_push(&hook.args, post_rewrite_hook);
+ argv_array_push(&hook.args, "rebase");
+ /* we don't care if this hook failed */
+ run_command(&hook);
+ }
}
strbuf_release(&buf);
--
2.11.0.rc3.windows.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox