From: Johannes Schindelin <johannes.schindelin@gmx.de>
To: git@vger.kernel.org
Cc: Johannes Schindelin <johannes.schindelin@gmx.de>,
Junio C Hamano <gitster@pobox.com>,
Alban Gruin <alban.gruin@gmail.com>,
Pratik Karki <predatoramigo@gmail.com>,
Christian Couder <christian.couder@gmail.com>,
Stefan Beller <sbeller@google.com>,
Wink Saville <wink@saville.com>
Subject: [PATCH 2/6] sequencer: learn about the special "fake root commit" handling
Date: Sat, 28 Apr 2018 00:31:13 +0200 [thread overview]
Message-ID: <42db734a98059fcfd67627aecc93cc8f0439fd37.1524868165.git.johannes.schindelin@gmx.de> (raw)
In-Reply-To: <cover.1524868165.git.johannes.schindelin@gmx.de>
When an interactive rebase wants to recreate a root commit, it
- first creates a new, empty root commit,
- checks it out,
- converts the next `pick` command so that it amends the empty root
commit
Introduce support in the sequencer to handle such an empty root commit,
by looking for the file <GIT_DIR>/rebase-merge/squash-onto; if it exists
and contains a commit name, the sequencer will compare the HEAD to said
root commit, and if identical, a new root commit will be created.
While converting scripted code into proper, portable C, we also do away
with the old "amend with an empty commit message, then cherry-pick
without committing, then amend again" dance and replace it with code
that uses the internal API properly to do exactly what we want: create a
new root commit.
To keep the implementation simple, we always spawn `git commit` to create
new root commits.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
sequencer.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++--
sequencer.h | 4 ++
2 files changed, 105 insertions(+), 3 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 90c8218aa9a..fc124596b53 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -125,6 +125,12 @@ static GIT_PATH_FUNC(rebase_path_rewritten_list, "rebase-merge/rewritten-list")
static GIT_PATH_FUNC(rebase_path_rewritten_pending,
"rebase-merge/rewritten-pending")
+/*
+ * The path of the file containig the OID of the "squash onto" commit, i.e.
+ * the dummy commit used for `reset [new root]`.
+ */
+static GIT_PATH_FUNC(rebase_path_squash_onto, "rebase-merge/squash-onto")
+
/*
* The path of the file listing refs that need to be deleted after the rebase
* finishes. This is used by the `label` command to record the need for cleanup.
@@ -470,7 +476,8 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
- to, unborn ? &null_oid : from,
+ to, unborn && !is_rebase_i(opts) ?
+ &null_oid : from,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -692,6 +699,42 @@ static char *get_author(const char *message)
return NULL;
}
+static const char *read_author_ident(struct strbuf *buf)
+{
+ char *p, *p2;
+
+ if (strbuf_read_file(buf, rebase_path_author_script(), 256) <= 0)
+ return NULL;
+
+ for (p = buf->buf; *p; p++)
+ if (skip_prefix(p, "'\\\\''", (const char **)&p2))
+ strbuf_splice(buf, p - buf->buf, p2 - p, "'", 1);
+ else if (*p == '\'')
+ strbuf_splice(buf, p-- - buf->buf, 1, "", 0);
+
+ if (skip_prefix(buf->buf, "GIT_AUTHOR_NAME=", (const char **)&p)) {
+ strbuf_splice(buf, 0, p - buf->buf, "", 0);
+ p = strchr(buf->buf, '\n');
+ if (skip_prefix(p, "\nGIT_AUTHOR_EMAIL=", (const char **)&p2)) {
+ strbuf_splice(buf, p - buf->buf, p2 - p, " <", 2);
+ p = strchr(p, '\n');
+ if (skip_prefix(p, "\nGIT_AUTHOR_DATE=@",
+ (const char **)&p2)) {
+ strbuf_splice(buf, p - buf->buf, p2 - p,
+ "> ", 2);
+ p = strchr(p, '\n');
+ if (p) {
+ strbuf_setlen(buf, p - buf->buf);
+ return buf->buf;
+ }
+ }
+ }
+ }
+
+ warning(_("could not parse '%s'"), rebase_path_author_script());
+ return NULL;
+}
+
static const char staged_changes_advice[] =
N_("you have staged changes in your working tree\n"
"If these changes are meant to be squashed into the previous commit, run:\n"
@@ -711,6 +754,7 @@ N_("you have staged changes in your working tree\n"
#define AMEND_MSG (1<<2)
#define CLEANUP_MSG (1<<3)
#define VERIFY_MSG (1<<4)
+#define CREATE_ROOT_COMMIT (1<<5)
/*
* If we are cherry-pick, and if the merge did not result in
@@ -730,6 +774,40 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
struct child_process cmd = CHILD_PROCESS_INIT;
const char *value;
+ if (flags & CREATE_ROOT_COMMIT) {
+ struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
+ const char *author = is_rebase_i(opts) ?
+ read_author_ident(&script) : NULL;
+ struct object_id root_commit, *cache_tree_oid;
+ int res = 0;
+
+ if (!defmsg)
+ BUG("root commit without message");
+
+ if (!(cache_tree_oid = get_cache_tree_oid()))
+ res = -1;
+
+ if (!res)
+ res = strbuf_read_file(&msg, defmsg, 0);
+
+ if (res <= 0)
+ res = error_errno(_("could not read '%s'"), defmsg);
+ else
+ res = commit_tree(msg.buf, msg.len, cache_tree_oid,
+ NULL, &root_commit, author,
+ opts->gpg_sign);
+
+ strbuf_release(&msg);
+ strbuf_release(&script);
+ if (!res) {
+ update_ref(NULL, "CHERRY_PICK_HEAD", &root_commit, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR);
+ res = update_ref(NULL, "HEAD", &root_commit, NULL, 0,
+ UPDATE_REFS_MSG_ON_ERR);
+ }
+ return res < 0 ? error(_("writing root commit")) : 0;
+ }
+
cmd.git_cmd = 1;
if (is_rebase_i(opts)) {
@@ -1216,7 +1294,8 @@ static int do_commit(const char *msg_file, const char *author,
{
int res = 1;
- if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG)) {
+ if (!(flags & EDIT_MSG) && !(flags & VERIFY_MSG) &&
+ !(flags & CREATE_ROOT_COMMIT)) {
struct object_id oid;
struct strbuf sb = STRBUF_INIT;
@@ -1369,6 +1448,12 @@ static int is_fixup(enum todo_command command)
return command == TODO_FIXUP || command == TODO_SQUASH;
}
+/* Does this command create a (non-merge) commit? */
+static int is_pick_or_similar(enum todo_command command)
+{
+ return command <= TODO_SQUASH;
+}
+
static int update_squash_messages(enum todo_command command,
struct commit *commit, struct replay_opts *opts)
{
@@ -1523,7 +1608,14 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
return error(_("your index file is unmerged."));
} else {
unborn = get_oid("HEAD", &head);
- if (unborn)
+ /* Do we want to generate a root commit? */
+ if (is_pick_or_similar(command) && opts->have_squash_onto &&
+ !oidcmp(&head, &opts->squash_onto)) {
+ if (is_fixup(command))
+ return error(_("cannot fixup root commit"));
+ flags |= CREATE_ROOT_COMMIT;
+ unborn = 1;
+ } else if (unborn)
oidcpy(&head, the_hash_algo->empty_tree);
if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
NULL, 0))
@@ -2136,6 +2228,12 @@ static int read_populate_opts(struct replay_opts *opts)
read_strategy_opts(opts, &buf);
strbuf_release(&buf);
+ if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
+ if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
+ return error(_("unusable squash-onto"));
+ opts->have_squash_onto = 1;
+ }
+
return 0;
}
diff --git a/sequencer.h b/sequencer.h
index d9570d92b11..4b2717881fa 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -44,6 +44,10 @@ struct replay_opts {
char **xopts;
size_t xopts_nr, xopts_alloc;
+ /* placeholder commit for -i --root */
+ struct object_id squash_onto;
+ int have_squash_onto;
+
/* Only used by REPLAY_NONE */
struct rev_info *revs;
};
--
2.17.0.windows.1.33.gfcbb1fa0445
next prev parent reply other threads:[~2018-04-27 22:31 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-04-27 22:29 [PATCH 0/6] Let the sequencer handle `git rebase -i --root` Johannes Schindelin
2018-04-27 22:30 ` [PATCH 1/6] sequencer: extract helper to update active_cache_tree Johannes Schindelin
2018-04-28 15:28 ` Stefan Beller
2018-04-27 22:31 ` Johannes Schindelin [this message]
2018-04-28 16:11 ` [PATCH 2/6] sequencer: learn about the special "fake root commit" handling Stefan Beller
2018-04-29 12:33 ` Johannes Schindelin
2018-04-29 21:44 ` Stefan Beller
2018-04-27 22:31 ` [PATCH 3/6] rebase -i --root: let the sequencer handle even the initial part Johannes Schindelin
2018-04-28 16:19 ` Stefan Beller
2018-04-29 12:34 ` Johannes Schindelin
2018-04-27 22:31 ` [PATCH 4/6] sequencer: allow introducing new root commits Johannes Schindelin
2018-04-27 22:31 ` [PATCH 5/6] rebase --rebase-merges: a "merge" into a new root is a fast-forward Johannes Schindelin
2018-04-27 22:31 ` [PATCH 6/6] rebase --rebase-merges: root commits can be cousins, too Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 0/6] Let the sequencer handle `git rebase -i --root` Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 1/6] sequencer: extract helper to update active_cache_tree Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 2/6] sequencer: learn about the special "fake root commit" handling Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 3/6] rebase -i --root: let the sequencer handle even the initial part Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 4/6] sequencer: allow introducing new root commits Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 5/6] rebase --rebase-merges: a "merge" into a new root is a fast-forward Johannes Schindelin
2018-05-03 23:01 ` [PATCH v2 6/6] rebase --rebase-merges: root commits can be cousins, too Johannes Schindelin
2018-05-04 19:55 ` [PATCH v2 0/6] Let the sequencer handle `git rebase -i --root` Stefan Beller
2018-05-05 19:24 ` Johannes Schindelin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=42db734a98059fcfd67627aecc93cc8f0439fd37.1524868165.git.johannes.schindelin@gmx.de \
--to=johannes.schindelin@gmx.de \
--cc=alban.gruin@gmail.com \
--cc=christian.couder@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=predatoramigo@gmail.com \
--cc=sbeller@google.com \
--cc=wink@saville.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).