[PATCH v2 12/18] builtin rebase: support `--exec`

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for the `--exec` option which takes a shell
command-line as argument. This argument will be appended as an `exec
` command after each line in the todo list that creates a commit in
the final history.  commands.

Note: while the shell script version of `git rebase` assigned the empty
string to `cmd` by default, we *unset* it here because the code looks
nicer and it does not change the behavior.

The `--exec` option requires `--interactive` machinery.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 1e35b82d9a..2547be9efe 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -93,6 +93,7 @@ struct rebase_options {
int autosquash;
char *gpg_sign_opt;
int autostash;
+   char *cmd;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -346,6 +347,7 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
+   add_var(_snippet, "cmd", opts->cmd);
 
switch (opts->type) {
case REBASE_AM:
@@ -619,6 +621,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
const char *gpg_sign = NULL;
int opt_c = -1;
struct string_list whitespace = STRING_LIST_INIT_NODUP;
+   struct string_list exec = STRING_LIST_INIT_NODUP;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -691,6 +694,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
REBASE_AM),
OPT_BOOL(0, "autostash", ,
 N_("automatically stash/stash pop before and after")),
+   OPT_STRING_LIST('x', "exec", , N_("exec"),
+   N_("add exec lines after each commit of the "
+  "editable list")),
OPT_END(),
};
 
@@ -915,6 +921,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
}
 
+   if (exec.nr) {
+   int i;
+
+   imply_interactive(, "--exec");
+
+   strbuf_reset();
+   for (i = 0; i < exec.nr; i++)
+   strbuf_addf(, "exec %s\n", exec.items[i].string);
+   options.cmd = xstrdup(buf.buf);
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1197,5 +1214,6 @@ cleanup:
strbuf_release();
free(options.head_name);
free(options.gpg_sign_opt);
+   free(options.cmd);
return ret;
 }
-- 
gitgitgadget



[PATCH v2 17/18] builtin rebase: add support for custom merge strategies

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

When running a rebase in non-am mode, it uses the recursive merge to
cherry-pick the commits, and the rebase command allows to configure
the merge strategy to be used in this operation.

This commit adds that support to the builtin rebase.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 57 
 1 file changed, 57 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6c101e1260..847c7daf1c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -96,6 +96,7 @@ struct rebase_options {
char *cmd;
int allow_empty_message;
int rebase_merges, rebase_cousins;
+   char *strategy, *strategy_opts;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -217,6 +218,22 @@ static int read_basic_state(struct rebase_options *opts)
opts->gpg_sign_opt = xstrdup(buf.buf);
}
 
+   if (file_exists(state_dir_path("strategy", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("strategy", opts), ))
+   return -1;
+   free(opts->strategy);
+   opts->strategy = xstrdup(buf.buf);
+   }
+
+   if (file_exists(state_dir_path("strategy_opts", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("strategy_opts", opts), ))
+   return -1;
+   free(opts->strategy_opts);
+   opts->strategy_opts = xstrdup(buf.buf);
+   }
+
strbuf_release();
 
return 0;
@@ -356,6 +373,8 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->rebase_merges ? "t" : "");
add_var(_snippet, "rebase_cousins",
opts->rebase_cousins ? "t" : "");
+   add_var(_snippet, "strategy", opts->strategy);
+   add_var(_snippet, "strategy_opts", opts->strategy_opts);
 
switch (opts->type) {
case REBASE_AM:
@@ -633,6 +652,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
int fork_point = -1;
+   struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -717,6 +737,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
PARSE_OPT_OPTARG, NULL, (intptr_t)""},
OPT_BOOL(0, "fork-point", _point,
 N_("use 'merge-base --fork-point' to refine 
upstream")),
+   OPT_STRING('s', "strategy", ,
+  N_("strategy"), N_("use the given merge strategy")),
+   OPT_STRING_LIST('X', "strategy-option", _options,
+   N_("option"),
+   N_("pass the argument through to the merge "
+  "strategy")),
OPT_END(),
};
 
@@ -963,6 +989,37 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
imply_interactive(, "--rebase-merges");
}
 
+   if (strategy_options.nr) {
+   int i;
+
+   if (!options.strategy)
+   options.strategy = "recursive";
+
+   strbuf_reset();
+   for (i = 0; i < strategy_options.nr; i++)
+   strbuf_addf(, " --%s",
+   strategy_options.items[i].string);
+   options.strategy_opts = xstrdup(buf.buf);
+   }
+
+   if (options.strategy) {
+   options.strategy = xstrdup(options.strategy);
+   switch (options.type) {
+   case REBASE_AM:
+   die(_("--strategy requires --merge or --interactive"));
+   case REBASE_MERGE:
+   case REBASE_INTERACTIVE:
+   case REBASE_PRESERVE_MERGES:
+   /* compatible */
+   break;
+   case REBASE_UNSPECIFIED:
+   options.type = REBASE_MERGE;
+   break;
+   default:
+   BUG("unhandled rebase type (%d)", options.type);
+   }
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 16/18] builtin rebase: support `fork-point` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for `--fork-point` and `--no-fork-point`.
This is converted as-is from `git-legacy-rebase.sh`.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index bcacffda33..6c101e1260 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -632,6 +632,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
+   int fork_point = -1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -714,6 +715,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("mode"),
N_("try to rebase merges instead of skipping them"),
PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+   OPT_BOOL(0, "fork-point", _point,
+N_("use 'merge-base --fork-point' to refine 
upstream")),
OPT_END(),
};
 
@@ -1062,6 +1065,14 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
} else
BUG("unexpected number of arguments left to parse");
 
+   if (fork_point > 0) {
+   struct commit *head =
+   lookup_commit_reference(the_repository,
+   _head);
+   options.restrict_revision =
+   get_fork_point(options.upstream_name, head);
+   }
+
if (read_index(the_repository->index) < 0)
die(_("could not read index"));
 
-- 
gitgitgadget



[PATCH v2 11/18] builtin rebase: support `--autostash` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

To support `--autostash` we introduce a function `apply_autostash()`
just like in `git-legacy-rebase.sh`.

Rather than refactoring and using the same function that exists in
`sequencer.c`, we go a different route here, to avoid clashes with
the sister GSoC project that turns the interactive rebase into a
builtin.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 117 +++
 1 file changed, 109 insertions(+), 8 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 63ffe5e1ff..1e35b82d9a 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -63,12 +63,6 @@ static int use_builtin_rebase(void)
return ret;
 }
 
-static int apply_autostash(void)
-{
-   warning("TODO");
-   return 0;
-}
-
 struct rebase_options {
enum rebase_type type;
const char *state_dir;
@@ -98,6 +92,7 @@ struct rebase_options {
int keep_empty;
int autosquash;
char *gpg_sign_opt;
+   int autostash;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -224,13 +219,56 @@ static int read_basic_state(struct rebase_options *opts)
return 0;
 }
 
+static int apply_autostash(struct rebase_options *opts)
+{
+   const char *path = state_dir_path("autostash", opts);
+   struct strbuf autostash = STRBUF_INIT;
+   struct child_process stash_apply = CHILD_PROCESS_INIT;
+
+   if (!file_exists(path))
+   return 0;
+
+   if (read_one(state_dir_path("autostash", opts), ))
+   return error(_("Could not read '%s'"), path);
+   argv_array_pushl(_apply.args,
+"stash", "apply", autostash.buf, NULL);
+   stash_apply.git_cmd = 1;
+   stash_apply.no_stderr = stash_apply.no_stdout =
+   stash_apply.no_stdin = 1;
+   if (!run_command(_apply))
+   printf(_("Applied autostash.\n"));
+   else {
+   struct argv_array args = ARGV_ARRAY_INIT;
+   int res = 0;
+
+   argv_array_pushl(,
+"stash", "store", "-m", "autostash", "-q",
+autostash.buf, NULL);
+   if (run_command_v_opt(args.argv, RUN_GIT_CMD))
+   res = error(_("Cannot store %s"), autostash.buf);
+   argv_array_clear();
+   strbuf_release();
+   if (res)
+   return res;
+
+   fprintf(stderr,
+   _("Applying autostash resulted in conflicts.\n"
+ "Your changes are safe in the stash.\n"
+ "You can run \"git stash pop\" or \"git stash drop\" "
+ "at any time.\n"));
+   }
+
+   strbuf_release();
+   return 0;
+}
+
 static int finish_rebase(struct rebase_options *opts)
 {
struct strbuf dir = STRBUF_INIT;
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
 
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-   apply_autostash();
+   apply_autostash(opts);
close_all_packs(the_repository->objects);
/*
 * We ignore errors in 'gc --auto', since the
@@ -345,7 +383,7 @@ static int run_specific_rebase(struct rebase_options *opts)
} else if (status == 2) {
struct strbuf dir = STRBUF_INIT;
 
-   apply_autostash();
+   apply_autostash(opts);
strbuf_addstr(, opts->state_dir);
remove_dir_recursively(, 0);
strbuf_release();
@@ -480,6 +518,11 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return 0;
}
 
+   if (!strcmp(var, "rebase.autostash")) {
+   opts->autostash = git_config_bool(var, value);
+   return 0;
+   }
+
return git_default_config(var, value, data);
 }
 
@@ -646,6 +689,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("whitespace"), N_("passed to 'git apply'")),
OPT_SET_INT('C', 0, _c, N_("passed to 'git apply'"),
REBASE_AM),
+   OPT_BOOL(0, "autostash", ,
+N_("automatically stash/stash pop before and after")),
OPT_END(),
};
 
@@ -975,6 +1020,62 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (read_index(the_repository->index) < 0)
die(_("could not read index"));
 
+   if (options.autostash) {
+   struct lock_file lock_file = LOCK_INIT;
+   int fd

[PATCH v2 13/18] builtin rebase: support `--allow-empty-message` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit introduces the `--allow-empty-message` option to
`builtin/rebase.c`. The motivation behind this option is: if there are
empty messages (which is not allowed in Git by default, but can be
imported from different version control systems), the rebase will fail.

Using `--allow-empty-message` overrides that behaviour which will allow
the commits having empty messages to continue in rebase operation.

Note: a very recent change made this the default in the shell scripted
`git rebase`, therefore the builtin rebase does the same.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 2547be9efe..3e37603da4 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -94,6 +94,7 @@ struct rebase_options {
char *gpg_sign_opt;
int autostash;
char *cmd;
+   int allow_empty_message;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -348,6 +349,8 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(_snippet, "cmd", opts->cmd);
+   add_var(_snippet, "allow_empty_message",
+   opts->allow_empty_message ?  "--allow-empty-message" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -598,6 +601,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.flags = REBASE_NO_QUIET,
.git_am_opt = STRBUF_INIT,
.allow_rerere_autoupdate  = -1,
+   .allow_empty_message = 1,
};
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
@@ -697,6 +701,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_STRING_LIST('x', "exec", , N_("exec"),
N_("add exec lines after each commit of the "
   "editable list")),
+   OPT_BOOL(0, "allow-empty-message",
+_empty_message,
+N_("allow rebasing commits with empty messages")),
OPT_END(),
};
 
-- 
gitgitgadget



[PATCH v2 18/18] builtin rebase: support --root

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This option allows to rebase entire histories up to, and including, the
root commit.

The conversion from the shell script is straight-forward, apart from
the fact that we do not have to write an empty tree in C.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 31 +--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 847c7daf1c..71b92658ec 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -76,6 +76,7 @@ struct rebase_options {
const char *revisions;
const char *switch_to;
int root;
+   struct object_id *squash_onto;
struct commit *restrict_revision;
int dont_finish_rebase;
enum {
@@ -375,6 +376,9 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->rebase_cousins ? "t" : "");
add_var(_snippet, "strategy", opts->strategy);
add_var(_snippet, "strategy_opts", opts->strategy_opts);
+   add_var(_snippet, "rebase_root", opts->root ? "t" : "");
+   add_var(_snippet, "squash_onto",
+   opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -653,6 +657,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
const char *rebase_merges = NULL;
int fork_point = -1;
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
+   struct object_id squash_onto;
+   char *squash_onto_name = NULL;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -743,6 +749,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("option"),
N_("pass the argument through to the merge "
   "strategy")),
+   OPT_BOOL(0, "root", ,
+N_("rebase all reachable commits up to the root(s)")),
OPT_END(),
};
 
@@ -1020,6 +1028,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
}
 
+   if (options.root && !options.onto_name)
+   imply_interactive(, "--root without --onto");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1058,8 +1069,22 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
options.upstream_arg = options.upstream_name;
-   } else
-   die("TODO: upstream for --root");
+   } else {
+   if (!options.onto_name) {
+   if (commit_tree("", 0, the_hash_algo->empty_tree, NULL,
+   _onto, NULL, NULL) < 0)
+   die(_("Could not create new root commit"));
+   options.squash_onto = _onto;
+   options.onto_name = squash_onto_name =
+   xstrdup(oid_to_hex(_onto));
+   }
+   options.upstream_name = NULL;
+   options.upstream = NULL;
+   if (argc > 1)
+   usage_with_options(builtin_rebase_usage,
+  builtin_rebase_options);
+   options.upstream_arg = "--root";
+   }
 
/* Make sure the branch to rebase onto is valid. */
if (!options.onto_name)
@@ -1207,6 +1232,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 */
if (can_fast_forward(options.onto, _head, _base) &&
!is_interactive() && !options.restrict_revision &&
+   options.upstream &&
!oidcmp(>object.oid, >object.oid)) {
int flag;
 
@@ -1311,5 +1337,6 @@ cleanup:
free(options.head_name);
free(options.gpg_sign_opt);
free(options.cmd);
+   free(squash_onto_name);
return ret;
 }
-- 
gitgitgadget


[PATCH v2 09/18] builtin rebase: support `--gpg-sign` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit introduces support for `--gpg-sign` option which is used
to GPG-sign commits.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 27 +++
 1 file changed, 27 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index c80eebfbd2..0ab86b7c4c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -97,6 +97,7 @@ struct rebase_options {
int allow_rerere_autoupdate;
int keep_empty;
int autosquash;
+   char *gpg_sign_opt;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -209,6 +210,15 @@ static int read_basic_state(struct rebase_options *opts)
} else
opts->allow_rerere_autoupdate = -1;
 
+   if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("gpg_sign_opt", opts),
+   ))
+   return -1;
+   free(opts->gpg_sign_opt);
+   opts->gpg_sign_opt = xstrdup(buf.buf);
+   }
+
strbuf_release();
 
return 0;
@@ -297,6 +307,7 @@ static int run_specific_rebase(struct rebase_options *opts)
"--rerere-autoupdate" : "--no-rerere-autoupdate");
add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
+   add_var(_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
 
switch (opts->type) {
case REBASE_AM:
@@ -462,6 +473,13 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return 0;
}
 
+   if (!strcmp(var, "commit.gpgsign")) {
+   free(opts->gpg_sign_opt);
+   opts->gpg_sign_opt = git_config_bool(var, value) ?
+   xstrdup("-S") : NULL;
+   return 0;
+   }
+
return git_default_config(var, value, data);
 }
 
@@ -555,6 +573,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int committer_date_is_author_date = 0;
int ignore_date = 0;
int ignore_whitespace = 0;
+   const char *gpg_sign = NULL;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -619,6 +638,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_BOOL(0, "autosquash", ,
 N_("move commits that begin with "
"squash!/fixup! under -i")),
+   OPT_STRING('S', "gpg-sign", _sign,
+  N_("gpg-sign?"), N_("GPG-sign commits")),
OPT_END(),
};
 
@@ -821,6 +842,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (options.keep_empty)
imply_interactive(, "--keep-empty");
 
+   if (gpg_sign) {
+   free(options.gpg_sign_opt);
+   options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1046,5 +1072,6 @@ run_rebase:
 cleanup:
strbuf_release();
free(options.head_name);
+   free(options.gpg_sign_opt);
return ret;
 }
-- 
gitgitgadget



[PATCH v2 15/18] merge-base --fork-point: extract libified function

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

We need this functionality in the builtin rebase.

Note: to make this function truly reusable, we have to switch the call
get_merges_many_dirty() to get_merges_many() because we want the commit
flags to be reset (otherwise, subsequent get_merge_bases() calls would
obtain incorrect results). This did not matter when the function was
called in `git rev-parse --fork-point` because in that command, the
process definitely did not traverse any commits before exiting.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/merge-base.c | 81 
 commit.c | 81 
 commit.h |  2 ++
 3 files changed, 89 insertions(+), 75 deletions(-)

diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 08d91b1f0c..790ceaeed6 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -110,54 +110,12 @@ static int handle_is_ancestor(int argc, const char **argv)
return 1;
 }
 
-struct rev_collect {
-   struct commit **commit;
-   int nr;
-   int alloc;
-   unsigned int initial : 1;
-};
-
-static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
-{
-   struct commit *commit;
-
-   if (is_null_oid(oid))
-   return;
-
-   commit = lookup_commit(the_repository, oid);
-   if (!commit ||
-   (commit->object.flags & TMP_MARK) ||
-   parse_commit(commit))
-   return;
-
-   ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
-   revs->commit[revs->nr++] = commit;
-   commit->object.flags |= TMP_MARK;
-}
-
-static int collect_one_reflog_ent(struct object_id *ooid, struct object_id 
*noid,
- const char *ident, timestamp_t timestamp,
- int tz, const char *message, void *cbdata)
-{
-   struct rev_collect *revs = cbdata;
-
-   if (revs->initial) {
-   revs->initial = 0;
-   add_one_commit(ooid, revs);
-   }
-   add_one_commit(noid, revs);
-   return 0;
-}
-
 static int handle_fork_point(int argc, const char **argv)
 {
struct object_id oid;
char *refname;
+   struct commit *derived, *fork_point;
const char *commitname;
-   struct rev_collect revs;
-   struct commit *derived;
-   struct commit_list *bases;
-   int i, ret = 0;
 
switch (dwim_ref(argv[0], strlen(argv[0]), , )) {
case 0:
@@ -173,41 +131,14 @@ static int handle_fork_point(int argc, const char **argv)
die("Not a valid object name: '%s'", commitname);
 
derived = lookup_commit_reference(the_repository, );
-   memset(, 0, sizeof(revs));
-   revs.initial = 1;
-   for_each_reflog_ent(refname, collect_one_reflog_ent, );
 
-   if (!revs.nr && !get_oid(refname, ))
-   add_one_commit(, );
+   fork_point = get_fork_point(refname, derived);
 
-   for (i = 0; i < revs.nr; i++)
-   revs.commit[i]->object.flags &= ~TMP_MARK;
-
-   bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit);
-
-   /*
-* There should be one and only one merge base, when we found
-* a common ancestor among reflog entries.
-*/
-   if (!bases || bases->next) {
-   ret = 1;
-   goto cleanup_return;
-   }
-
-   /* And the found one must be one of the reflog entries */
-   for (i = 0; i < revs.nr; i++)
-   if (>item->object == [i]->object)
-   break; /* found */
-   if (revs.nr <= i) {
-   ret = 1; /* not found */
-   goto cleanup_return;
-   }
-
-   printf("%s\n", oid_to_hex(>item->object.oid));
+   if (!fork_point)
+   return 1;
 
-cleanup_return:
-   free_commit_list(bases);
-   return ret;
+   printf("%s\n", oid_to_hex(_point->object.oid));
+   return 0;
 }
 
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
diff --git a/commit.c b/commit.c
index 30d1af2b20..a3fc77a4eb 100644
--- a/commit.c
+++ b/commit.c
@@ -17,6 +17,7 @@
 #include "sha1-lookup.h"
 #include "wt-status.h"
 #include "advice.h"
+#include "refs.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char 
*buf, size_t len, const char **);
 
@@ -958,6 +959,86 @@ static struct commit_list *merge_bases_many(struct commit 
*one, int n, struct co
return result;
 }
 
+struct rev_collect {
+   struct commit **commit;
+   int nr;
+   int alloc;
+   unsigned int initial : 1;
+};
+
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
+{
+   struct commit *commit;
+
+   if (is_null_oid(oid))
+   return;
+
+   co

[PATCH v2 10/18] builtin rebase: support `-C` and `--whitespace=`

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit converts more code from the shell script version to the
builtin rebase. In this instance, we just have to be careful to
keep support for passing multiple `--whitespace` options, as the
shell script version does so, too.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 23 +++
 1 file changed, 23 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 0ab86b7c4c..63ffe5e1ff 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -574,6 +574,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int ignore_date = 0;
int ignore_whitespace = 0;
const char *gpg_sign = NULL;
+   int opt_c = -1;
+   struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -640,6 +642,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
"squash!/fixup! under -i")),
OPT_STRING('S', "gpg-sign", _sign,
   N_("gpg-sign?"), N_("GPG-sign commits")),
+   OPT_STRING_LIST(0, "whitespace", ,
+   N_("whitespace"), N_("passed to 'git apply'")),
+   OPT_SET_INT('C', 0, _c, N_("passed to 'git apply'"),
+   REBASE_AM),
OPT_END(),
};
 
@@ -847,6 +853,23 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
}
 
+   if (opt_c >= 0)
+   strbuf_addf(_am_opt, " -C%d", opt_c);
+
+   if (whitespace.nr) {
+   int i;
+
+   for (i = 0; i < whitespace.nr; i++) {
+   const char *item = whitespace.items[i].string;
+
+   strbuf_addf(_am_opt, " --whitespace=%s",
+   item);
+
+   if ((!strcmp(item, "fix")) || (!strcmp(item, "strip")))
+   options.flags |= REBASE_FORCE;
+   }
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 14/18] builtin rebase: support --rebase-merges[=[no-]rebase-cousins]

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

The mode to rebase non-linear branches is now supported by the builtin
rebase, too.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 3e37603da4..bcacffda33 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -95,6 +95,7 @@ struct rebase_options {
int autostash;
char *cmd;
int allow_empty_message;
+   int rebase_merges, rebase_cousins;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -351,6 +352,10 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "cmd", opts->cmd);
add_var(_snippet, "allow_empty_message",
opts->allow_empty_message ?  "--allow-empty-message" : "");
+   add_var(_snippet, "rebase_merges",
+   opts->rebase_merges ? "t" : "");
+   add_var(_snippet, "rebase_cousins",
+   opts->rebase_cousins ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -626,6 +631,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int opt_c = -1;
struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct string_list exec = STRING_LIST_INIT_NODUP;
+   const char *rebase_merges = NULL;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -704,6 +710,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_BOOL(0, "allow-empty-message",
 _empty_message,
 N_("allow rebasing commits with empty messages")),
+   {OPTION_STRING, 'r', "rebase-merges", _merges,
+   N_("mode"),
+   N_("try to rebase merges instead of skipping them"),
+   PARSE_OPT_OPTARG, NULL, (intptr_t)""},
OPT_END(),
};
 
@@ -939,6 +949,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.cmd = xstrdup(buf.buf);
}
 
+   if (rebase_merges) {
+   if (!*rebase_merges)
+   ; /* default mode; do nothing */
+   else if (!strcmp("rebase-cousins", rebase_merges))
+   options.rebase_cousins = 1;
+   else if (strcmp("no-rebase-cousins", rebase_merges))
+   die(_("Unknown mode: %s"), rebase_merges);
+   options.rebase_merges = 1;
+   imply_interactive(, "--rebase-merges");
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 08/18] builtin rebase: support `--autosquash`

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for the `--autosquash` option which is used to
automatically squash the commits marked as `squash` or `fixup` in their
messages. This is converted following `git-legacy-rebase.sh` closely.

This option can also be configured via the Git config setting
rebase.autosquash. To support this, we also add a custom
rebase_config() function in this commit that will be used instead (and
falls back to) git_default_config().

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 9c00f3568f..c80eebfbd2 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -96,6 +96,7 @@ struct rebase_options {
int signoff;
int allow_rerere_autoupdate;
int keep_empty;
+   int autosquash;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -295,6 +296,7 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->allow_rerere_autoupdate ?
"--rerere-autoupdate" : "--no-rerere-autoupdate");
add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+   add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -455,6 +457,11 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return 0;
}
 
+   if (!strcmp(var, "rebase.autosquash")) {
+   opts->autosquash = git_config_bool(var, value);
+   return 0;
+   }
+
return git_default_config(var, value, data);
 }
 
@@ -609,6 +616,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
"conflict")),
OPT_BOOL('k', "keep-empty", _empty,
 N_("preserve empty commits during rebase")),
+   OPT_BOOL(0, "autosquash", ,
+N_("move commits that begin with "
+   "squash!/fixup! under -i")),
OPT_END(),
};
 
-- 
gitgitgadget



[PATCH v2 07/18] builtin rebase: support `keep-empty` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

The `--keep-empty` option can be used to keep the commits that do not
change anything from its parents in the result.

While the scripted version uses `interactive_rebase=implied` to indicate
that the rebase needs to use the `git-rebase--interactive` backend in
non-interactive mode as fallback when figuring out which backend to use,
the C version needs to use a different route because the backend will
already be chosen during the `parse_options()` call.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 42ee040da3..9c00f3568f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -95,6 +95,7 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
+   int keep_empty;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -103,6 +104,23 @@ static int is_interactive(struct rebase_options *opts)
opts->type == REBASE_PRESERVE_MERGES;
 }
 
+static void imply_interactive(struct rebase_options *opts, const char *option)
+{
+   switch (opts->type) {
+   case REBASE_AM:
+   die(_("%s requires an interactive rebase"), option);
+   break;
+   case REBASE_INTERACTIVE:
+   case REBASE_PRESERVE_MERGES:
+   break;
+   case REBASE_MERGE:
+   /* we silently *upgrade* --merge to --interactive if needed */
+   default:
+   opts->type = REBASE_INTERACTIVE; /* implied */
+   break;
+   }
+}
+
 /* Returns the filename prefixed by the state_dir */
 static const char *state_dir_path(const char *filename, struct rebase_options 
*opts)
 {
@@ -276,6 +294,7 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->allow_rerere_autoupdate < 0 ? "" :
opts->allow_rerere_autoupdate ?
"--rerere-autoupdate" : "--no-rerere-autoupdate");
+   add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -588,6 +607,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 _rerere_autoupdate,
 N_("allow rerere to update index  with resolved "
"conflict")),
+   OPT_BOOL('k', "keep-empty", _empty,
+N_("preserve empty commits during rebase")),
OPT_END(),
};
 
@@ -787,6 +808,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.flags |= REBASE_FORCE;
}
 
+   if (options.keep_empty)
+   imply_interactive(, "--keep-empty");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 06/18] builtin rebase: support `ignore-date` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for `--ignore-date` which is passed to `git am`
to easily change the dates of the rebased commits.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 7490d215ef..42ee040da3 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -527,6 +527,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
int committer_date_is_author_date = 0;
+   int ignore_date = 0;
int ignore_whitespace = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -550,6 +551,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_BOOL(0, "committer-date-is-author-date",
 _date_is_author_date,
 N_("passed to 'git am'")),
+   OPT_BOOL(0, "ignore-date", _date,
+N_("passed to 'git am'")),
OPT_BIT('f', "force-rebase", ,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
@@ -779,6 +782,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (ignore_whitespace)
strbuf_addstr(_am_opt, " --ignore-whitespace");
 
+   if (ignore_date) {
+   strbuf_addstr(_am_opt, " --ignore-date");
+   options.flags |= REBASE_FORCE;
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 02/18] builtin rebase: support --signoff

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for `--signoff` which is used to add a
`Signed-off-by` trailer to all the rebased commits. The actual
handling is left to the rebase backends.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 17 +
 1 file changed, 17 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fc9b5a8a60..a491481120 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -93,6 +93,7 @@ struct rebase_options {
} flags;
struct strbuf git_am_opt;
const char *action;
+   int signoff;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -168,6 +169,11 @@ static int read_basic_state(struct rebase_options *opts)
if (file_exists(state_dir_path("verbose", opts)))
opts->flags |= REBASE_VERBOSE;
 
+   if (file_exists(state_dir_path("signoff", opts))) {
+   opts->signoff = 1;
+   opts->flags |= REBASE_FORCE;
+   }
+
strbuf_release();
 
return 0;
@@ -249,6 +255,7 @@ static int run_specific_rebase(struct rebase_options *opts)
if (opts->switch_to)
add_var(_snippet, "switch_to", opts->switch_to);
add_var(_snippet, "action", opts->action ? opts->action : "");
+   add_var(_snippet, "signoff", opts->signoff ? "--signoff" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -513,6 +520,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
{OPTION_NEGBIT, 'n', "no-stat", , NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+   OPT_BOOL(0, "signoff", ,
+N_("add a Signed-off-by: line to each commit")),
OPT_BIT('f', "force-rebase", ,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
@@ -745,6 +754,14 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
break;
}
 
+   if (options.signoff) {
+   if (options.type == REBASE_PRESERVE_MERGES)
+   die("cannot combine '--signoff' with "
+   "'--preserve-merges'");
+   strbuf_addstr(_am_opt, " --signoff");
+   options.flags |= REBASE_FORCE;
+   }
+
if (!options.root) {
if (argc < 1)
die("TODO: handle @{upstream}");
-- 
gitgitgadget



[PATCH v2 05/18] builtin rebase: support `ignore-whitespace` option

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for the `--ignore-whitespace` option
of the rebase command. This option is simply passed to the
`--am` backend.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index eef16206c2..7490d215ef 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -527,6 +527,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
int committer_date_is_author_date = 0;
+   int ignore_whitespace = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -542,6 +543,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
{OPTION_NEGBIT, 'n', "no-stat", , NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+   OPT_BOOL(0, "ignore-whitespace", _whitespace,
+N_("passed to 'git apply'")),
OPT_BOOL(0, "signoff", ,
 N_("add a Signed-off-by: line to each commit")),
OPT_BOOL(0, "committer-date-is-author-date",
@@ -773,6 +776,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.flags |= REBASE_FORCE;
}
 
+   if (ignore_whitespace)
+   strbuf_addstr(_am_opt, " --ignore-whitespace");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 03/18] builtin rebase: support --rerere-autoupdate

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

The `--rerere-autoupdate` option allows rerere to update the index with
resolved conflicts. This commit follows closely the equivalent part of
`git-legacy-rebase.sh`.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 25 +
 1 file changed, 25 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index a491481120..1729d2d9e2 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -94,6 +94,7 @@ struct rebase_options {
struct strbuf git_am_opt;
const char *action;
int signoff;
+   int allow_rerere_autoupdate;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -174,6 +175,21 @@ static int read_basic_state(struct rebase_options *opts)
opts->flags |= REBASE_FORCE;
}
 
+   if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
+   ))
+   return -1;
+   if (!strcmp(buf.buf, "--rerere-autoupdate"))
+   opts->allow_rerere_autoupdate = 1;
+   else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
+   opts->allow_rerere_autoupdate = 0;
+   else
+   warning(_("ignoring invalid allow_rerere_autoupdate: "
+ "'%s'"), buf.buf);
+   } else
+   opts->allow_rerere_autoupdate = -1;
+
strbuf_release();
 
return 0;
@@ -256,6 +272,10 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "switch_to", opts->switch_to);
add_var(_snippet, "action", opts->action ? opts->action : "");
add_var(_snippet, "signoff", opts->signoff ? "--signoff" : "");
+   add_var(_snippet, "allow_rerere_autoupdate",
+   opts->allow_rerere_autoupdate < 0 ? "" :
+   opts->allow_rerere_autoupdate ?
+   "--rerere-autoupdate" : "--no-rerere-autoupdate");
 
switch (opts->type) {
case REBASE_AM:
@@ -488,6 +508,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.type = REBASE_UNSPECIFIED,
.flags = REBASE_NO_QUIET,
.git_am_opt = STRBUF_INIT,
+   .allow_rerere_autoupdate  = -1,
};
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
@@ -553,6 +574,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_SET_INT('p', "preserve-merges", ,
N_("try to recreate merges instead of ignoring "
   "them"), REBASE_PRESERVE_MERGES),
+   OPT_BOOL(0, "rerere-autoupdate",
+_rerere_autoupdate,
+N_("allow rerere to update index  with resolved "
+   "conflict")),
OPT_END(),
};
 
-- 
gitgitgadget



[PATCH v2 04/18] builtin rebase: support --committer-date-is-author-date

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This option is simply handed down to `git am` by way of setting the
`git_am_opt` variable that is handled by the `git-rebase--am` backend.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 1729d2d9e2..eef16206c2 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -526,6 +526,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_EDIT_TODO,
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
+   int committer_date_is_author_date = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -543,6 +544,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
OPT_BOOL(0, "signoff", ,
 N_("add a Signed-off-by: line to each commit")),
+   OPT_BOOL(0, "committer-date-is-author-date",
+_date_is_author_date,
+N_("passed to 'git am'")),
OPT_BIT('f', "force-rebase", ,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
@@ -763,6 +767,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (!(options.flags & REBASE_NO_QUIET))
strbuf_addstr(_am_opt, " -q");
 
+   if (committer_date_is_author_date) {
+   strbuf_addstr(_am_opt,
+ " --committer-date-is-author-date");
+   options.flags |= REBASE_FORCE;
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 01/18] builtin rebase: allow selecting the rebase "backend"

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

With this commit the builtin rebase supports selecting the "rebase
backends" (or "type") `interactive`, `preserve-merges`, and `merge`.

The `state_dir` was already handled according to the rebase type in a
previous commit.

Note that there is one quirk in the shell script: `--interactive`
followed by `--merge` won't reset the type to "merge" but keeps the type
as "interactive". And as t3418 tests this explicitly, we have to support
it in the builtin rebase, too.

Likewise, `--interactive` followed by `--preserve-merges` makes it an
"explicitly interactive" rebase, i.e. a rebase that should show the todo
list, while `--preserve-merges` alone is not interactive (and t5520
tests for this via `git pull --rebase=preserve`).

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 37 +
 1 file changed, 37 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 75c4ac66e0..fc9b5a8a60 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -452,6 +452,29 @@ static int can_fast_forward(struct commit *onto, struct 
object_id *head_oid,
return res && is_linear_history(onto, head);
 }
 
+/* -i followed by -m is still -i */
+static int parse_opt_merge(const struct option *opt, const char *arg, int 
unset)
+{
+   struct rebase_options *opts = opt->value;
+
+   if (!is_interactive(opts))
+   opts->type = REBASE_MERGE;
+
+   return 0;
+}
+
+/* -i followed by -p is still explicitly interactive, but -p alone is not */
+static int parse_opt_interactive(const struct option *opt, const char *arg,
+int unset)
+{
+   struct rebase_options *opts = opt->value;
+
+   opts->type = REBASE_INTERACTIVE;
+   opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
+
+   return 0;
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -510,6 +533,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_CMDMODE(0, "show-current-patch", ,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
+   { OPTION_CALLBACK, 'm', "merge", , NULL,
+   N_("use merging strategies to rebase"),
+   PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+   parse_opt_merge },
+   { OPTION_CALLBACK, 'i', "interactive", , NULL,
+   N_("let the user edit the list of commits to rebase"),
+   PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+   parse_opt_interactive },
+   OPT_SET_INT('p', "preserve-merges", ,
+   N_("try to recreate merges instead of ignoring "
+  "them"), REBASE_PRESERVE_MERGES),
OPT_END(),
};
 
@@ -884,6 +918,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
diff_flush();
}
 
+   if (is_interactive())
+   goto run_rebase;
+
/* Detach HEAD and reset the tree */
if (options.flags & REBASE_NO_QUIET)
printf(_("First, rewinding head to replay your work on top of "
-- 
gitgitgadget



[PATCH v2 07/11] builtin rebase: try to fast forward when possible

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

In this commit, we add support to fast forward.

Note: we will need the merge base later, therefore the call to
can_fast_forward() really needs to be the first one when testing whether
we can skip the rebase entirely (otherwise, it would make more sense to
skip the possibly expensive operation if, say, running an interactive
rebase).

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 72 
 1 file changed, 72 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index afef0b0046..d67df28efc 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -20,6 +20,7 @@
 #include "commit.h"
 #include "diff.h"
 #include "wt-status.h"
+#include "revision.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -89,6 +90,12 @@ struct rebase_options {
struct strbuf git_am_opt;
 };
 
+static int is_interactive(struct rebase_options *opts)
+{
+   return opts->type == REBASE_INTERACTIVE ||
+   opts->type == REBASE_PRESERVE_MERGES;
+}
+
 /* Returns the filename prefixed by the state_dir */
 static const char *state_dir_path(const char *filename, struct rebase_options 
*opts)
 {
@@ -334,6 +341,46 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return git_default_config(var, value, data);
 }
 
+/*
+ * Determines whether the commits in from..to are linear, i.e. contain
+ * no merge commits. This function *expects* `from` to be an ancestor of
+ * `to`.
+ */
+static int is_linear_history(struct commit *from, struct commit *to)
+{
+   while (to && to != from) {
+   parse_commit(to);
+   if (!to->parents)
+   return 1;
+   if (to->parents->next)
+   return 0;
+   to = to->parents->item;
+   }
+   return 1;
+}
+
+static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
+   struct object_id *merge_base)
+{
+   struct commit *head = lookup_commit(the_repository, head_oid);
+   struct commit_list *merge_bases;
+   int res;
+
+   if (!head)
+   return 0;
+
+   merge_bases = get_merge_bases(onto, head);
+   if (merge_bases && !merge_bases->next) {
+   oidcpy(merge_base, _bases->item->object.oid);
+   res = !oidcmp(merge_base, >object.oid);
+   } else {
+   oidcpy(merge_base, _oid);
+   res = 0;
+   }
+   free_commit_list(merge_bases);
+   return res && is_linear_history(onto, head);
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -489,6 +536,31 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
goto cleanup;
}
 
+   /*
+* Now we are rebasing commits upstream..orig_head (or with --root,
+* everything leading up to orig_head) on top of onto.
+*/
+
+   /*
+* Check if we are already based on onto with linear history,
+* but this should be done only when upstream and onto are the same
+* and if this is not an interactive rebase.
+*/
+   if (can_fast_forward(options.onto, _head, _base) &&
+   !is_interactive() && !options.restrict_revision &&
+   !oidcmp(>object.oid, >object.oid)) {
+   int flag;
+
+   if (!(options.flags & REBASE_NO_QUIET))
+   ; /* be quiet */
+   else if (!strcmp(branch_name, "HEAD") &&
+resolve_ref_unsafe("HEAD", 0, NULL, ))
+   puts(_("HEAD is up to date, rebase forced."));
+   else
+   printf(_("Current branch %s is up to date, rebase "
+"forced.\n"), branch_name);
+   }
+
/* If a hook exists, give it a chance to interrupt*/
if (!ok_to_skip_pre_rebase &&
run_hook_le(NULL, "pre-rebase", options.upstream_arg,
-- 
gitgitgadget



[PATCH v2 10/11] builtin rebase: only store fully-qualified refs in `options.head_name`

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

When running a rebase on a detached HEAD, we currently store the string
"detached HEAD" in options.head_name. That is a faithful translation of
the shell script version, and we still kind of need it for the purposes of
the scripted backends.

It is poor style for C, though, where we would really only want a valid,
fully-qualified ref name as value, and NULL for detached HEADs, using
"detached HEAD" for display only. Make it so.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 11 ---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index d45f8f9008..afc75fe731 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -169,7 +169,8 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "upstream_name", opts->upstream_name);
add_var(_snippet, "upstream",
 oid_to_hex(>upstream->object.oid));
-   add_var(_snippet, "head_name", opts->head_name);
+   add_var(_snippet, "head_name",
+   opts->head_name ? opts->head_name : "detached HEAD");
add_var(_snippet, "orig_head", oid_to_hex(>orig_head));
add_var(_snippet, "onto", oid_to_hex(>onto->object.oid));
add_var(_snippet, "onto_name", opts->onto_name);
@@ -251,6 +252,9 @@ static int reset_head(struct object_id *oid, const char 
*action,
*old_orig = NULL, oid_old_orig;
int ret = 0;
 
+   if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+   BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
return -1;
 
@@ -558,7 +562,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 * branch_name -- branch/commit being rebased, or
 *HEAD (already detached)
 * orig_head -- commit object name of tip of the branch before rebasing
-* head_name -- refs/heads/ or "detached HEAD"
+* head_name -- refs/heads/ or NULL (detached HEAD)
 */
if (argc > 0)
 die("TODO: handle switch_to");
@@ -575,7 +579,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
branch_name = options.head_name;
 
} else {
-   options.head_name = xstrdup("detached HEAD");
+   free(options.head_name);
+   options.head_name = NULL;
branch_name = "HEAD";
}
if (get_oid("HEAD", _head))
-- 
gitgitgadget



[PATCH v2 04/11] builtin rebase: support --quiet

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit introduces a rebase option `--quiet`. While `--quiet` is
commonly perceived as opposite to `--verbose`, this is not the case for
the rebase command: both `--quiet` and `--verbose` default to `false` if
neither `--quiet` nor `--verbose` is present.

Despite the default being `false` for both verbose and quiet mode,
passing the `--quiet` option will turn off verbose mode, and `--verbose`
will turn off quiet mode.

This patch introduces the `flags` bit field, with `REBASE_NO_QUIET`
as first user (with many more to come).

We do *not* use `REBASE_QUIET` here for an important reason: To keep the
implementation simple, this commit introduces `--no-quiet` instead of
`--quiet`, so that a single `OPT_NEGBIT()` can turn on quiet mode and
turn off verbose and diffstat mode at the same time. Likewise, the
companion commit which will introduce support for `--verbose` will have
a single `OPT_BIT()` that turns off quiet mode and turns on verbose and
diffstat mode at the same time.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index b79f9b0a9f..19fa4d3fc4 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,10 @@ struct rebase_options {
int root;
struct commit *restrict_revision;
int dont_finish_rebase;
+   enum {
+   REBASE_NO_QUIET = 1<<0,
+   } flags;
+   struct strbuf git_am_opt;
 };
 
 /* Returns the filename prefixed by the state_dir */
@@ -159,6 +163,9 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "revisions", opts->revisions);
add_var(_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(>restrict_revision->object.oid) : NULL);
+   add_var(_snippet, "GIT_QUIET",
+   opts->flags & REBASE_NO_QUIET ? "" : "t");
+   add_var(_snippet, "git_am_opt", opts->git_am_opt.buf);
 
switch (opts->type) {
case REBASE_AM:
@@ -308,6 +315,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 {
struct rebase_options options = {
.type = REBASE_UNSPECIFIED,
+   .flags = REBASE_NO_QUIET,
+   .git_am_opt = STRBUF_INIT,
};
const char *branch_name;
int ret, flags;
@@ -321,6 +330,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
   N_("rebase onto given branch instead of upstream")),
OPT_BOOL(0, "no-verify", _to_skip_pre_rebase,
 N_("allow pre-rebase hook to run")),
+   OPT_NEGBIT('q', "quiet", ,
+  N_("be quiet. implies --no-stat"),
+  REBASE_NO_QUIET),
OPT_END(),
};
 
@@ -357,6 +369,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_rebase_usage,
   builtin_rebase_options);
 
+   if (!(options.flags & REBASE_NO_QUIET))
+   strbuf_addstr(_am_opt, " -q");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
gitgitgadget



[PATCH v2 02/11] builtin rebase: support `git rebase --onto A...B`

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit implements support for an --onto argument that is actually a
"symmetric range" i.e. `...`.

The equivalent shell script version of the code offers two different
error messages for the cases where there is no merge base vs more than
one merge base.

Though it would be nice to retain this distinction, dropping it makes it
possible to simply use the `get_oid_mb()` function. Besides, it happens
rarely in real-world scenarios.

Therefore, in the interest of keeping the code less complex, let's just
use that function, and live with an error message that does not
distinguish between those two error conditions.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 8 +++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 742ed31498..38c496dd10 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -17,6 +17,7 @@
 #include "unpack-trees.h"
 #include "lockfile.h"
 #include "parse-options.h"
+#include "commit.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -311,6 +312,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int ret, flags;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
+   struct object_id merge_base;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
-   die("TODO");
+   if (get_oid_mb(options.onto_name, _base) < 0)
+   die(_("'%s': need exactly one merge base"),
+   options.onto_name);
+   options.onto = lookup_commit_or_die(_base,
+   options.onto_name);
} else {
options.onto = peel_committish(options.onto_name);
if (!options.onto)
-- 
gitgitgadget



[PATCH v2 11/11] builtin rebase: support `git rebase `

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit adds support for `switch-to` which is used to switch to the
target branch if needed. The equivalent codes found in shell script
`git-legacy-rebase.sh` is converted to builtin `rebase.c`.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 48 
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index afc75fe731..e817956d96 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,7 @@ struct rebase_options {
struct commit *onto;
const char *onto_name;
const char *revisions;
+   const char *switch_to;
int root;
struct commit *restrict_revision;
int dont_finish_rebase;
@@ -186,6 +187,8 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->flags & REBASE_DIFFSTAT ? "t" : "");
add_var(_snippet, "force_rebase",
opts->flags & REBASE_FORCE ? "t" : "");
+   if (opts->switch_to)
+   add_var(_snippet, "switch_to", opts->switch_to);
 
switch (opts->type) {
case REBASE_AM:
@@ -564,9 +567,23 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 * orig_head -- commit object name of tip of the branch before rebasing
 * head_name -- refs/heads/ or NULL (detached HEAD)
 */
-   if (argc > 0)
-die("TODO: handle switch_to");
-   else {
+   if (argc == 1) {
+   /* Is it "rebase other branchname" or "rebase other commit"? */
+   branch_name = argv[0];
+   options.switch_to = argv[0];
+
+   /* Is it a local branch? */
+   strbuf_reset();
+   strbuf_addf(, "refs/heads/%s", branch_name);
+   if (!read_ref(buf.buf, _head))
+   options.head_name = xstrdup(buf.buf);
+   /* If not is it a valid ref (branch or commit)? */
+   else if (!get_oid(branch_name, _head))
+   options.head_name = NULL;
+   else
+   die(_("fatal: no such branch/commit '%s'"),
+   branch_name);
+   } else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
@@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
if (get_oid("HEAD", _head))
die(_("Could not resolve HEAD to a revision"));
-   }
+   } else
+   BUG("unexpected number of arguments left to parse");
 
if (read_index(the_repository->index) < 0)
die(_("could not read index"));
@@ -612,6 +630,28 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int flag;
 
if (!(options.flags & REBASE_FORCE)) {
+   /* Lazily switch to the target branch if needed... */
+   if (options.switch_to) {
+   struct object_id oid;
+
+   if (get_oid(options.switch_to, ) < 0) {
+   ret = !!error(_("could not parse '%s'"),
+ options.switch_to);
+   goto cleanup;
+   }
+
+   strbuf_reset();
+   strbuf_addf(, "rebase: checkout %s",
+   options.switch_to);
+   if (reset_head(, "checkout",
+  options.head_name, 0) < 0) {
+   ret = !!error(_("could not switch to "
+   "%s"),
+ options.switch_to);
+   goto cleanup;
+   }
+   }
+
if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
-- 
gitgitgadget


[PATCH v2 09/11] builtin rebase: start a new rebase only if none is in progress

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

To run a new rebase, there needs to be a check to assure that no other
rebase is in progress. New rebase operation cannot start until an
ongoing rebase operation completes or is terminated.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 48 +++-
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 8a7bf3d468..d45f8f9008 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -87,6 +87,7 @@ struct rebase_options {
REBASE_VERBOSE = 1<<1,
REBASE_DIFFSTAT = 1<<2,
REBASE_FORCE = 1<<3,
+   REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
struct strbuf git_am_opt;
 };
@@ -392,10 +393,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.git_am_opt = STRBUF_INIT,
};
const char *branch_name;
-   int ret, flags;
+   int ret, flags, in_progress = 0;
int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
+   struct strbuf buf = STRBUF_INIT;
struct object_id merge_base;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -447,6 +449,30 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 
git_config(rebase_config, );
 
+   if (is_directory(apply_dir())) {
+   options.type = REBASE_AM;
+   options.state_dir = apply_dir();
+   } else if (is_directory(merge_dir())) {
+   strbuf_reset();
+   strbuf_addf(, "%s/rewritten", merge_dir());
+   if (is_directory(buf.buf)) {
+   options.type = REBASE_PRESERVE_MERGES;
+   options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+   } else {
+   strbuf_reset();
+   strbuf_addf(, "%s/interactive", merge_dir());
+   if(file_exists(buf.buf)) {
+   options.type = REBASE_INTERACTIVE;
+   options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+   } else
+   options.type = REBASE_MERGE;
+   }
+   options.state_dir = merge_dir();
+   }
+
+   if (options.type != REBASE_UNSPECIFIED)
+   in_progress = 1;
+
argc = parse_options(argc, argv, prefix,
 builtin_rebase_options,
 builtin_rebase_usage, 0);
@@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_rebase_usage,
   builtin_rebase_options);
 
+   /* Make sure no rebase is in progress */
+   if (in_progress) {
+   const char *last_slash = strrchr(options.state_dir, '/');
+   const char *state_dir_base =
+   last_slash ? last_slash + 1 : options.state_dir;
+   const char *cmd_live_rebase =
+   "git rebase (--continue | --abort | --skip)";
+   strbuf_reset();
+   strbuf_addf(, "rm -fr \"%s\"", options.state_dir);
+   die(_("It seems that there is already a %s directory, and\n"
+ "I wonder if you are in the middle of another rebase.  "
+ "If that is the\n"
+ "case, please try\n\t%s\n"
+ "If that is not the case, please\n\t%s\n"
+ "and run me again.  I am stopping in case you still "
+ "have something\n"
+ "valuable there.\n"),
+   state_dir_base, cmd_live_rebase, buf.buf);
+   }
+
if (!(options.flags & REBASE_NO_QUIET))
strbuf_addstr(_am_opt, " -q");
 
-- 
gitgitgadget



[PATCH v2 03/11] builtin rebase: handle the pre-rebase hook and --no-verify

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit converts the equivalent part of the shell script
`git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and
to interrupt the rebase with error if the hook fails.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 38c496dd10..b79f9b0a9f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -70,6 +70,7 @@ struct rebase_options {
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
+   const char *upstream_arg;
char *head_name;
struct object_id orig_head;
struct commit *onto;
@@ -310,6 +311,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
};
const char *branch_name;
int ret, flags;
+   int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
struct object_id merge_base;
@@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_STRING(0, "onto", _name,
   N_("revision"),
   N_("rebase onto given branch instead of upstream")),
+   OPT_BOOL(0, "no-verify", _to_skip_pre_rebase,
+N_("allow pre-rebase hook to run")),
OPT_END(),
};
 
@@ -382,6 +386,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.upstream = peel_committish(options.upstream_name);
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
+   options.upstream_arg = options.upstream_name;
} else
die("TODO: upstream for --root");
 
@@ -430,6 +435,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("Could not resolve HEAD to a revision"));
}
 
+   /* If a hook exists, give it a chance to interrupt*/
+   if (!ok_to_skip_pre_rebase &&
+   run_hook_le(NULL, "pre-rebase", options.upstream_arg,
+   argc ? argv[0] : NULL, NULL))
+   die(_("The pre-rebase hook refused to rebase."));
+
strbuf_addf(, "rebase: checkout %s", options.onto_name);
if (reset_head(>object.oid, "checkout", NULL, 1))
die(_("Could not detach HEAD"));
-- 
gitgitgadget



[PATCH v2 01/11] builtin rebase: support --onto

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

The `--onto` option is important, as it allows to rebase a range of
commits onto a different base commit (which gave the command its odd
name: "rebase").

This commit introduces options parsing so that different options can
be added in future commits.

Note: As this commit introduces to the parse_options() call (which
"eats" argv[0]), the argc is now expected to be lower by one after this
patch, compared to before this patch: argv[0] no longer refers to the
command name, but to the first (non-option) command-line parameter.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 35 ++-
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index e695d8a430..742ed31498 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -16,6 +16,16 @@
 #include "cache-tree.h"
 #include "unpack-trees.h"
 #include "lockfile.h"
+#include "parse-options.h"
+
+static char const * const builtin_rebase_usage[] = {
+   N_("git rebase [-i] [options] [--exec ] [--onto ] "
+   "[] []"),
+   N_("git rebase [-i] [options] [--exec ] [--onto ] "
+   "--root []"),
+   N_("git rebase --continue | --abort | --skip | --edit-todo"),
+   NULL
+};
 
 static GIT_PATH_FUNC(apply_dir, "rebase-apply")
 static GIT_PATH_FUNC(merge_dir, "rebase-merge")
@@ -301,6 +311,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int ret, flags;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
+   struct option builtin_rebase_options[] = {
+   OPT_STRING(0, "onto", _name,
+  N_("revision"),
+  N_("rebase onto given branch instead of upstream")),
+   OPT_END(),
+   };
 
/*
 * NEEDSWORK: Once the builtin rebase has been tested enough
@@ -318,13 +334,22 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
BUG("sane_execvp() returned???");
}
 
-   if (argc != 2)
-   die(_("Usage: %s "), argv[0]);
+   if (argc == 2 && !strcmp(argv[1], "-h"))
+   usage_with_options(builtin_rebase_usage,
+  builtin_rebase_options);
+
prefix = setup_git_directory();
trace_repo_setup(prefix);
setup_work_tree();
 
git_config(git_default_config, NULL);
+   argc = parse_options(argc, argv, prefix,
+builtin_rebase_options,
+builtin_rebase_usage, 0);
+
+   if (argc > 2)
+   usage_with_options(builtin_rebase_usage,
+  builtin_rebase_options);
 
switch (options.type) {
case REBASE_MERGE:
@@ -343,10 +368,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
 
if (!options.root) {
-   if (argc < 2)
+   if (argc < 1)
die("TODO: handle @{upstream}");
else {
-   options.upstream_name = argv[1];
+   options.upstream_name = argv[0];
argc--;
argv++;
if (!strcmp(options.upstream_name, "-"))
@@ -377,7 +402,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 * orig_head -- commit object name of tip of the branch before rebasing
 * head_name -- refs/heads/ or "detached HEAD"
 */
-   if (argc > 1)
+   if (argc > 0)
 die("TODO: handle switch_to");
else {
/* Do not need to switch branches, we are already on it. */
-- 
gitgitgadget



[PATCH v2 05/11] builtin rebase: support the `verbose` and `diffstat` options

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit introduces support for the `-v` and `--stat` options of
rebase.

The --stat option can also be configured via the Git config setting
rebase.stat. To support this, we also add a custom rebase_config()
function in this commit that will be used instead of (and falls back to
calling) git_default_config().

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 60 ++--
 1 file changed, 58 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 19fa4d3fc4..2d3f1d65fb 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -18,6 +18,7 @@
 #include "lockfile.h"
 #include "parse-options.h"
 #include "commit.h"
+#include "diff.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -81,6 +82,8 @@ struct rebase_options {
int dont_finish_rebase;
enum {
REBASE_NO_QUIET = 1<<0,
+   REBASE_VERBOSE = 1<<1,
+   REBASE_DIFFSTAT = 1<<2,
} flags;
struct strbuf git_am_opt;
 };
@@ -166,6 +169,10 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "GIT_QUIET",
opts->flags & REBASE_NO_QUIET ? "" : "t");
add_var(_snippet, "git_am_opt", opts->git_am_opt.buf);
+   add_var(_snippet, "verbose",
+   opts->flags & REBASE_VERBOSE ? "t" : "");
+   add_var(_snippet, "diffstat",
+   opts->flags & REBASE_DIFFSTAT ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -311,6 +318,21 @@ static int reset_head(struct object_id *oid, const char 
*action,
return ret;
 }
 
+static int rebase_config(const char *var, const char *value, void *data)
+{
+   struct rebase_options *opts = data;
+
+   if (!strcmp(var, "rebase.stat")) {
+   if (git_config_bool(var, value))
+   opts->flags |= REBASE_DIFFSTAT;
+   else
+   opts->flags &= !REBASE_DIFFSTAT;
+   return 0;
+   }
+
+   return git_default_config(var, value, data);
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -332,7 +354,13 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", ,
   N_("be quiet. implies --no-stat"),
-  REBASE_NO_QUIET),
+  REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+   OPT_BIT('v', "verbose", ,
+   N_("display a diffstat of what changed upstream"),
+   REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+   {OPTION_NEGBIT, 'n', "no-stat", , NULL,
+   N_("do not show diffstat of what changed upstream"),
+   PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
OPT_END(),
};
 
@@ -360,7 +388,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
trace_repo_setup(prefix);
setup_work_tree();
 
-   git_config(git_default_config, NULL);
+   git_config(rebase_config, );
+
argc = parse_options(argc, argv, prefix,
 builtin_rebase_options,
 builtin_rebase_usage, 0);
@@ -456,6 +485,33 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
argc ? argv[0] : NULL, NULL))
die(_("The pre-rebase hook refused to rebase."));
 
+   if (options.flags & REBASE_DIFFSTAT) {
+   struct diff_options opts;
+
+   if (options.flags & REBASE_VERBOSE)
+   printf(_("Changes from %s to %s:\n"),
+   oid_to_hex(_base),
+   oid_to_hex(>object.oid));
+
+   /* We want color (if set), but no pager */
+   diff_setup();
+   opts.stat_width = -1; /* use full terminal width */
+   opts.stat_graph_width = -1; /* respect statGraphWidth config */
+   opts.output_format |=
+   DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+   opts.detect_rename = DIFF_DETECT_RENAME;
+   diff_setup_done();
+   diff_tree_oid(_base, >object.oid,
+ "", );
+   diffcore_std();
+   diff_flush();
+   }
+
+   /* Detach HEAD and reset the tree *

[PATCH v2 08/11] builtin rebase: support --force-rebase

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

In this commit, we add support to `--force-rebase` option. The
equivalent part of the shell script found in `git-legacy-rebase.sh` is
converted as faithfully as possible to C.

The --force-rebase option ensures that the rebase does not simply
fast-forward even if it could.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 22 +-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index d67df28efc..8a7bf3d468 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -86,6 +86,7 @@ struct rebase_options {
REBASE_NO_QUIET = 1<<0,
REBASE_VERBOSE = 1<<1,
REBASE_DIFFSTAT = 1<<2,
+   REBASE_FORCE = 1<<3,
} flags;
struct strbuf git_am_opt;
 };
@@ -181,6 +182,8 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->flags & REBASE_VERBOSE ? "t" : "");
add_var(_snippet, "diffstat",
opts->flags & REBASE_DIFFSTAT ? "t" : "");
+   add_var(_snippet, "force_rebase",
+   opts->flags & REBASE_FORCE ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -409,6 +412,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
{OPTION_NEGBIT, 'n', "no-stat", , NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+   OPT_BIT('f', "force-rebase", ,
+   N_("cherry-pick all commits, even if unchanged"),
+   REBASE_FORCE),
+   OPT_BIT(0, "no-ff", ,
+   N_("cherry-pick all commits, even if unchanged"),
+   REBASE_FORCE),
OPT_END(),
};
 
@@ -551,7 +560,18 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
!oidcmp(>object.oid, >object.oid)) {
int flag;
 
-   if (!(options.flags & REBASE_NO_QUIET))
+   if (!(options.flags & REBASE_FORCE)) {
+   if (!(options.flags & REBASE_NO_QUIET))
+   ; /* be quiet */
+   else if (!strcmp(branch_name, "HEAD") &&
+resolve_ref_unsafe("HEAD", 0, NULL, ))
+   puts(_("HEAD is up to date."));
+   else
+   printf(_("Current branch %s is up to date.\n"),
+  branch_name);
+   ret = !!finish_rebase();
+   goto cleanup;
+   } else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
 resolve_ref_unsafe("HEAD", 0, NULL, ))
-- 
gitgitgadget



[PATCH v2 06/11] builtin rebase: require a clean worktree

2018-09-04 Thread Pratik Karki via GitGitGadget
From: Pratik Karki 

This commit reads the index of the repository for rebase and checks
whether the repository is ready for rebase.

Signed-off-by: Pratik Karki 
Signed-off-by: Johannes Schindelin 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 2d3f1d65fb..afef0b0046 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -19,6 +19,7 @@
 #include "parse-options.h"
 #include "commit.h"
 #include "diff.h"
+#include "wt-status.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -479,6 +480,15 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("Could not resolve HEAD to a revision"));
}
 
+   if (read_index(the_repository->index) < 0)
+   die(_("could not read index"));
+
+   if (require_clean_work_tree("rebase",
+   _("Please commit or stash them."), 1, 1)) {
+   ret = 1;
+   goto cleanup;
+   }
+
/* If a hook exists, give it a chance to interrupt*/
if (!ok_to_skip_pre_rebase &&
run_hook_le(NULL, "pre-rebase", options.upstream_arg,
@@ -528,6 +538,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 
ret = !!run_specific_rebase();
 
+cleanup:
strbuf_release();
free(options.head_name);
return ret;
-- 
gitgitgadget



[GSoC] [PATCH 0/1] default to builtin rebase

2018-08-08 Thread Pratik Karki
Since, all the conversion of shell script version of `git rebase` is done
as can be seen from the previous patch series, now we can default to the
builtin one. The builtin rebase can now handle every task done by the
original shell script `git rebase`. So, in this patch series, we turn the
builtin rebase `on` so that the rebase operations can be handled it by it.

This patch series is the continuation of
(https://public-inbox.org/git/20180808153635.19944-1-predatoram...@gmail.com/).

This is the sixth and final patch series for a builtin "git rebase".

If you like to view the development branch, you can view
(https://github.com/git/git/pull/505), where I have kept my commits up to date
and leveraged Travis(there is sporadic failures in t5520 for macos gcc and
isn't due to my patches) for extra testing other than my system.

These patch series are built on top of each other, i.e. they depend on this
order.

Pratik Karki (1):
  rebase: default to using the builtin rebase

 builtin/rebase.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

-- 
2.18.0


[PATCH 1/1] rebase: default to using the builtin rebase

2018-08-08 Thread Pratik Karki
Now that the builtin rebase is feature-complete, we should use it by
default. Let's keep the legacy scripted version around for the time
being; Once the builtin rebase is well-tested enough, we can remove
`git-legacy-rebase.sh`.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 4e69458161..c8d632b6f4 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -54,7 +54,7 @@ static int use_builtin_rebase(void)
cp.git_cmd = 1;
if (capture_command(, , 6)) {
strbuf_release();
-   return 0;
+   return 1;
}
 
strbuf_trim();
-- 
2.18.0



[PATCH 5/6] builtin rebase: use no-op editor when interactive is "implied"

2018-08-08 Thread Pratik Karki
Some options are only handled by the git-rebase--interactive backend,
even if run non-interactively. For this awkward situation (run
non-interactively, but use the interactive backend), the shell scripted
version of `git rebase` introduced the concept of an "implied
interactive rebase". All it does is to replace the editor by a dummy one
(`:` is the Unix command that takes arbitrary command-line parameters,
ignores them and simply exits with success).

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 04974dff6e..fb8ab5a177 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -383,6 +383,13 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "git_format_patch_opt",
opts->git_format_patch_opt.buf);
 
+   if (is_interactive(opts) &&
+   !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+   strbuf_addstr(_snippet,
+ "GIT_EDITOR=:; export GIT_EDITOR; ");
+   opts->autosquash = 0;
+   }
+
switch (opts->type) {
case REBASE_AM:
backend = "git-rebase--am";
-- 
2.18.0



[PATCH 3/6] builtin rebase: fast-forward to onto if it is a proper descendant

2018-08-08 Thread Pratik Karki
When trying to rebase onto a direct descendant of HEAD, we can
take a shortcut and fast-forward instead. This commit makes it so.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index c51b9d288a..1bb64e7cd7 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1367,6 +1367,24 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("Could not detach HEAD"));
strbuf_release();
 
+   /*
+* If the onto is a proper descendant of the tip of the branch, then
+* we just fast-forwarded.
+*/
+   strbuf_reset();
+   if (!oidcmp(_base, _head)) {
+   printf(_("Fast-forwarded %s to %s. \n"),
+   branch_name, options.onto_name);
+   strbuf_addf(, "rebase finished: %s onto %s",
+   options.head_name ? options.head_name : "detached HEAD",
+   oid_to_hex(>object.oid));
+   reset_head(NULL, "Fast-forwarded", options.head_name, 0,
+  "HEAD", msg.buf);
+   strbuf_release();
+   ret = !!finish_rebase();
+   goto cleanup;
+   }
+
strbuf_addf(, "%s..%s",
options.root ? oid_to_hex(>object.oid) :
(options.restrict_revision ?
-- 
2.18.0



[PATCH 1/6] builtin rebase: optionally auto-detect the upstream

2018-08-08 Thread Pratik Karki
The `git rebase` command, when called without the ``
command-line argument, automatically looks for the upstream
branch configured for the current branch.

With this commit, the builtin rebase learned that trick, too.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 44 +---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 94abaaa890..c5b2534717 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -622,6 +622,36 @@ static int parse_opt_interactive(const struct option *opt, 
const char *arg,
return 0;
 }
 
+static void NORETURN error_on_missing_default_upstream(void)
+{
+   struct branch *current_branch = branch_get(NULL);
+
+   printf(_("%s\n"
+"Please specify which branch you want to rebase against.\n"
+"See git-rebase(1) for details.\n"
+"\n"
+"git rebase ''\n"
+"\n"),
+   current_branch ? _("There is no tracking information for "
+   "the current branch.") :
+   _("You are not currently on a branch."));
+
+   if (current_branch) {
+   const char *remote = current_branch->remote_name;
+
+   if (!remote)
+   remote = _("");
+
+   printf(_("If you wish to set tracking information for this "
+"branch you can do so with:\n"
+"\n"
+"git branch --set-upstream-to=%s/ %s\n"
+"\n"),
+  remote, current_branch->name);
+   }
+   exit(1);
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -1056,9 +1086,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
 
if (!options.root) {
-   if (argc < 1)
-   die("TODO: handle @{upstream}");
-   else {
+   if (argc < 1) {
+   struct branch *branch;
+
+   branch = branch_get(NULL);
+   options.upstream_name = branch_get_upstream(branch,
+   NULL);
+   if (!options.upstream_name)
+   error_on_missing_default_upstream();
+   if (fork_point < 0)
+   fork_point = 1;
+   } else {
options.upstream_name = argv[0];
argc--;
argv++;
-- 
2.18.0



[PATCH 6/6] builtin rebase: error out on incompatible option/mode combinations

2018-08-08 Thread Pratik Karki
While working on the GSoC project to convert the rebase command to a
builtin, the rebase command learned to error out on certain command-line
option combinations that cannot work, such as --whitespace=fix with
--interactive.

This commit converts that code.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 41 +
 1 file changed, 41 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fb8ab5a177..4e69458161 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1098,6 +1098,28 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
break;
}
 
+   if (options.git_am_opt.len) {
+   const char *p;
+
+   /* all am options except -q are compatible only with --am */
+   strbuf_reset();
+   strbuf_addbuf(, _am_opt);
+   strbuf_addch(, ' ');
+   while ((p = strstr(buf.buf, " -q ")))
+   strbuf_splice(, p - buf.buf, 4, " ", 1);
+   strbuf_trim();
+
+   if (is_interactive() && buf.len)
+   die(_("error: cannot combine interactive options "
+ "(--interactive, --exec, --rebase-merges, "
+ "--preserve-merges, --keep-empty, --root + "
+ "--onto) with am options (%s)"), buf.buf);
+   if (options.type == REBASE_MERGE && buf.len)
+   die(_("error: cannot combine merge options (--merge, "
+ "--strategy, --strategy-option) with am options "
+ "(%s)"), buf.buf);
+   }
+
if (options.signoff) {
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
@@ -1106,6 +1128,25 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.flags |= REBASE_FORCE;
}
 
+   if (options.type == REBASE_PRESERVE_MERGES)
+   /*
+* Note: incompatibility with --signoff handled in signoff 
block above
+* Note: incompatibility with --interactive is just a strong 
warning;
+*   git-rebase.txt caveats with "unless you know what you 
are doing"
+*/
+   if (options.rebase_merges)
+   die(_("error: cannot combine '--preserve_merges' with "
+ "'--rebase-merges'"));
+
+   if (options.rebase_merges) {
+   if (strategy_options.nr)
+   die(_("error: cannot combine '--rebase_merges' with "
+ "'--strategy-option'"));
+   if (options.strategy)
+   die(_("error: cannot combine '--rebase_merges' with "
+ "'--strategy'"));
+   }
+
if (!options.root) {
if (argc < 1) {
struct branch *branch;
-- 
2.18.0



[GSoC] [PATCH 0/6] builtin rebase rest

2018-08-08 Thread Pratik Karki
This patch pretty much converts all what is remaining from the shell-script
version of `git rebase` to the builtin version. After the conversion of
actions and different options which are important for builtin rebase and have
been organized to make a patch series of their own. There were still some
leftover shell scripts on the original rebase and hence, this patch series
addresses those by converting them and this completes the builtin rebase.

It adds the functionality to the previous patch series,
(https://public-inbox.org/git/20180808152140.14585-1-predatoram...@gmail.com/).

This is the fifth patch series that brings us more closer to a builtin
"git rebase".

If you like to view the development branch, you can view
(https://github.com/git/git/pull/505), where I have kept my commits up to date
and leveraged Travis(there is sporadic failures in t5520 for macos gcc and
isn't due to my patches) for extra testing other than my system.

The next in line patch series which I'll be sending out today are:

default to builtin rebase: This will turn on the feature-complete builtin
rebase to on.

These patch series are built on top of each other, i.e. they depend on this
order.

Pratik Karki (6):
  builtin rebase: optionally auto-detect the upstream
  builtin rebase: optionally pass custom reflogs to reset_head()
  builtin rebase: fast-forward to onto if it is a proper descendant
  builtin rebase: show progress when connected to a terminal
  builtin rebase: use no-op editor when interactive is "implied"
  builtin rebase: error out on incompatible option/mode combinations

 builtin/rebase.c | 150 ++-
 1 file changed, 135 insertions(+), 15 deletions(-)

-- 
2.18.0



[PATCH 4/6] builtin rebase: show progress when connected to a terminal

2018-08-08 Thread Pratik Karki
In this commit, we pass `--progress` to the `format-patch` command if
stderr is connected to an interactive terminal, unless we're in quiet
mode.

This `--progress` option will be used in `format-patch` to show progress
reports on stderr as patches are generated.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 1bb64e7cd7..04974dff6e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -98,6 +98,7 @@ struct rebase_options {
int allow_empty_message;
int rebase_merges, rebase_cousins;
char *strategy, *strategy_opts;
+   struct strbuf git_format_patch_opt;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -379,6 +380,8 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "rebase_root", opts->root ? "t" : "");
add_var(_snippet, "squash_onto",
opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
+   add_var(_snippet, "git_format_patch_opt",
+   opts->git_format_patch_opt.buf);
 
switch (opts->type) {
case REBASE_AM:
@@ -667,6 +670,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.git_am_opt = STRBUF_INIT,
.allow_rerere_autoupdate  = -1,
.allow_empty_message = 1,
+   .git_format_patch_opt = STRBUF_INIT,
};
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
@@ -1068,6 +1072,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (options.root && !options.onto_name)
imply_interactive(, "--root without --onto");
 
+   if (isatty(2) && options.flags & REBASE_NO_QUIET)
+   strbuf_addstr(_format_patch_opt, " --progress");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 2/6] builtin rebase: optionally pass custom reflogs to reset_head()

2018-08-08 Thread Pratik Karki
In the next patch, we will make use of that in the code that
fast-forwards to `onto` whenever possible.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 33 +
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index c5b2534717..c51b9d288a 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -431,7 +431,8 @@ static int run_specific_rebase(struct rebase_options *opts)
 #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
 
 static int reset_head(struct object_id *oid, const char *action,
- const char *switch_to_branch, int detach_head)
+ const char *switch_to_branch, int detach_head,
+ const char *reflog_orig_head, const char *reflog_head)
 {
struct object_id head_oid;
struct tree_desc desc;
@@ -506,20 +507,26 @@ static int reset_head(struct object_id *oid, const char 
*action,
old_orig = _old_orig;
if (!get_oid("HEAD", _orig)) {
orig = _orig;
-   strbuf_addstr(, "updating ORIG_HEAD");
-   update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
+   if (!reflog_orig_head) {
+   strbuf_addstr(, "updating ORIG_HEAD");
+   reflog_orig_head = msg.buf;
+   }
+   update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
   UPDATE_REFS_MSG_ON_ERR);
} else if (old_orig)
delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
-   strbuf_setlen(, prefix_len);
-   strbuf_addstr(, "updating HEAD");
+   if (!reflog_head) {
+   strbuf_setlen(, prefix_len);
+   strbuf_addstr(, "updating HEAD");
+   reflog_head = msg.buf;
+   }
if (!switch_to_branch)
-   ret = update_ref(msg.buf, "HEAD", oid, orig, REF_NO_DEREF,
+   ret = update_ref(reflog_head, "HEAD", oid, orig, REF_NO_DEREF,
 UPDATE_REFS_MSG_ON_ERR);
else {
ret = create_symref("HEAD", switch_to_branch, msg.buf);
if (!ret)
-   ret = update_ref(msg.buf, "HEAD", oid, NULL, 0,
+   ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
 UPDATE_REFS_MSG_ON_ERR);
}
 
@@ -899,7 +906,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
rerere_clear(_rr);
string_list_clear(_rr, 1);
 
-   if (reset_head(NULL, "reset", NULL, 0) < 0)
+   if (reset_head(NULL, "reset", NULL, 0, NULL, NULL) < 0)
die(_("could not discard worktree changes"));
if (read_basic_state())
exit(1);
@@ -915,7 +922,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (read_basic_state())
exit(1);
if (reset_head(_head, "reset",
-  options.head_name, 0) < 0)
+  options.head_name, 0, NULL, NULL) < 0)
die(_("could not move back to %s"),
oid_to_hex(_head));
ret = finish_rebase();
@@ -1235,7 +1242,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
write_file(autostash, "%s", buf.buf);
printf(_("Created autostash: %s\n"), buf.buf);
if (reset_head(>object.oid, "reset --hard",
-  NULL, 0) < 0)
+  NULL, 0, NULL, NULL) < 0)
die(_("could not reset --hard"));
printf(_("HEAD is now at %s"),
   find_unique_abbrev(>object.oid,
@@ -1289,7 +1296,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
strbuf_addf(, "rebase: checkout %s",
options.switch_to);
if (reset_head(, "checkout",
-  options.head_name, 0) < 0) {
+  options.head_name, 0,
+  NULL, NULL) < 0) {
ret = !!error(_("could not switch to "
"%s"),
  options.switch_to);
@@ -1354,7 +1362,8 @@ int cmd_rebase(int argc, const 

[PATCH 16/18] builtin rebase: support `fork-point` option

2018-08-08 Thread Pratik Karki
This commit adds support for `--fork-point` and `--no-fork-point`.
This is converted as-is from `git-legacy-rebase.sh`.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 0b94d2daaa..72e64868b2 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -632,6 +632,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
+   int fork_point = -1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -714,6 +715,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("mode"),
N_("try to rebase merges instead of skipping them"),
PARSE_OPT_OPTARG, NULL, (intptr_t)""},
+   OPT_BOOL(0, "fork-point", _point,
+N_("use 'merge-base --fork-point' to refine 
upstream")),
OPT_END(),
};
 
@@ -1062,6 +1065,14 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
} else
BUG("unexpected number of arguments left to parse");
 
+   if (fork_point > 0) {
+   struct commit *head =
+   lookup_commit_reference(the_repository,
+   _head);
+   options.restrict_revision =
+   get_fork_point(options.upstream_name, head);
+   }
+
if (read_index(the_repository->index) < 0)
die(_("could not read index"));
 
-- 
2.18.0



[PATCH 14/18] builtin rebase: support --rebase-merges[=[no-]rebase-cousins]

2018-08-08 Thread Pratik Karki
The mode to rebase non-linear branches is now supported by the builtin
rebase, too.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 21 +
 1 file changed, 21 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index dfb1e6c25b..0b94d2daaa 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -95,6 +95,7 @@ struct rebase_options {
int autostash;
char *cmd;
int allow_empty_message;
+   int rebase_merges, rebase_cousins;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -351,6 +352,10 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "cmd", opts->cmd);
add_var(_snippet, "allow_empty_message",
opts->allow_empty_message ?  "--allow-empty-message" : "");
+   add_var(_snippet, "rebase_merges",
+   opts->rebase_merges ? "t" : "");
+   add_var(_snippet, "rebase_cousins",
+   opts->rebase_cousins ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -626,6 +631,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int opt_c = -1;
struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct string_list exec = STRING_LIST_INIT_NODUP;
+   const char *rebase_merges = NULL;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -704,6 +710,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_BOOL(0, "allow-empty-message",
 _empty_message,
 N_("allow rebasing commits with empty messages")),
+   {OPTION_STRING, 'r', "rebase-merges", _merges,
+   N_("mode"),
+   N_("try to rebase merges instead of skipping them"),
+   PARSE_OPT_OPTARG, NULL, (intptr_t)""},
OPT_END(),
};
 
@@ -939,6 +949,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.cmd = xstrdup(buf.buf);
}
 
+   if (rebase_merges) {
+   if (!*rebase_merges)
+   ; /* default mode; do nothing */
+   else if (!strcmp("rebase-cousins", rebase_merges))
+   options.rebase_cousins = 1;
+   else if (strcmp("no-rebase-cousins", rebase_merges))
+   die(_("Unknown mode: %s"), rebase_merges);
+   options.rebase_merges = 1;
+   imply_interactive(, "--rebase-merges");
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 17/18] builtin rebase: add support for custom merge strategies

2018-08-08 Thread Pratik Karki
When running a rebase in non-am mode, it uses the recursive merge to
cherry-pick the commits, and the rebase command allows to configure
the merge strategy to be used in this operation.

This commit adds that support to the builtin rebase.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 57 
 1 file changed, 57 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 72e64868b2..65e7be1c48 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -96,6 +96,7 @@ struct rebase_options {
char *cmd;
int allow_empty_message;
int rebase_merges, rebase_cousins;
+   char *strategy, *strategy_opts;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -217,6 +218,22 @@ static int read_basic_state(struct rebase_options *opts)
opts->gpg_sign_opt = xstrdup(buf.buf);
}
 
+   if (file_exists(state_dir_path("strategy", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("strategy", opts), ))
+   return -1;
+   free(opts->strategy);
+   opts->strategy = xstrdup(buf.buf);
+   }
+
+   if (file_exists(state_dir_path("strategy_opts", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("strategy_opts", opts), ))
+   return -1;
+   free(opts->strategy_opts);
+   opts->strategy_opts = xstrdup(buf.buf);
+   }
+
strbuf_release();
 
return 0;
@@ -356,6 +373,8 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->rebase_merges ? "t" : "");
add_var(_snippet, "rebase_cousins",
opts->rebase_cousins ? "t" : "");
+   add_var(_snippet, "strategy", opts->strategy);
+   add_var(_snippet, "strategy_opts", opts->strategy_opts);
 
switch (opts->type) {
case REBASE_AM:
@@ -633,6 +652,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
int fork_point = -1;
+   struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -717,6 +737,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
PARSE_OPT_OPTARG, NULL, (intptr_t)""},
OPT_BOOL(0, "fork-point", _point,
 N_("use 'merge-base --fork-point' to refine 
upstream")),
+   OPT_STRING('s', "strategy", ,
+  N_("strategy"), N_("use the given merge strategy")),
+   OPT_STRING_LIST('X', "strategy-option", _options,
+   N_("option"),
+   N_("pass the argument through to the merge "
+  "strategy")),
OPT_END(),
};
 
@@ -963,6 +989,37 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
imply_interactive(, "--rebase-merges");
}
 
+   if (strategy_options.nr) {
+   int i;
+
+   if (!options.strategy)
+   options.strategy = "recursive";
+
+   strbuf_reset();
+   for (i = 0; i < strategy_options.nr; i++)
+   strbuf_addf(, " --%s",
+   strategy_options.items[i].string);
+   options.strategy_opts = xstrdup(buf.buf);
+   }
+
+   if (options.strategy) {
+   options.strategy = xstrdup(options.strategy);
+   switch (options.type) {
+   case REBASE_AM:
+   die(_("--strategy requires --merge or --interactive"));
+   case REBASE_MERGE:
+   case REBASE_INTERACTIVE:
+   case REBASE_PRESERVE_MERGES:
+   /* compatible */
+   break;
+   case REBASE_UNSPECIFIED:
+   options.type = REBASE_MERGE;
+   break;
+   default:
+   BUG("unhandled rebase type (%d)", options.type);
+   }
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 15/18] merge-base --fork-point: extract libified function

2018-08-08 Thread Pratik Karki
We need this functionality in the builtin rebase.

Note: to make this function truly reusable, we have to switch the call
get_merges_many_dirty() to get_merges_many() because we want the commit
flags to be reset (otherwise, subsequent get_merge_bases() calls would
obtain incorrect results). This did not matter when the function was
called in `git rev-parse --fork-point` because in that command, the
process definitely did not traverse any commits before exiting.

Signed-off-by: Pratik Karki 
---
 builtin/merge-base.c | 81 
 commit.c | 81 
 commit.h |  2 ++
 3 files changed, 89 insertions(+), 75 deletions(-)

diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index 08d91b1f0c..790ceaeed6 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -110,54 +110,12 @@ static int handle_is_ancestor(int argc, const char **argv)
return 1;
 }
 
-struct rev_collect {
-   struct commit **commit;
-   int nr;
-   int alloc;
-   unsigned int initial : 1;
-};
-
-static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
-{
-   struct commit *commit;
-
-   if (is_null_oid(oid))
-   return;
-
-   commit = lookup_commit(the_repository, oid);
-   if (!commit ||
-   (commit->object.flags & TMP_MARK) ||
-   parse_commit(commit))
-   return;
-
-   ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
-   revs->commit[revs->nr++] = commit;
-   commit->object.flags |= TMP_MARK;
-}
-
-static int collect_one_reflog_ent(struct object_id *ooid, struct object_id 
*noid,
- const char *ident, timestamp_t timestamp,
- int tz, const char *message, void *cbdata)
-{
-   struct rev_collect *revs = cbdata;
-
-   if (revs->initial) {
-   revs->initial = 0;
-   add_one_commit(ooid, revs);
-   }
-   add_one_commit(noid, revs);
-   return 0;
-}
-
 static int handle_fork_point(int argc, const char **argv)
 {
struct object_id oid;
char *refname;
+   struct commit *derived, *fork_point;
const char *commitname;
-   struct rev_collect revs;
-   struct commit *derived;
-   struct commit_list *bases;
-   int i, ret = 0;
 
switch (dwim_ref(argv[0], strlen(argv[0]), , )) {
case 0:
@@ -173,41 +131,14 @@ static int handle_fork_point(int argc, const char **argv)
die("Not a valid object name: '%s'", commitname);
 
derived = lookup_commit_reference(the_repository, );
-   memset(, 0, sizeof(revs));
-   revs.initial = 1;
-   for_each_reflog_ent(refname, collect_one_reflog_ent, );
 
-   if (!revs.nr && !get_oid(refname, ))
-   add_one_commit(, );
+   fork_point = get_fork_point(refname, derived);
 
-   for (i = 0; i < revs.nr; i++)
-   revs.commit[i]->object.flags &= ~TMP_MARK;
-
-   bases = get_merge_bases_many_dirty(derived, revs.nr, revs.commit);
-
-   /*
-* There should be one and only one merge base, when we found
-* a common ancestor among reflog entries.
-*/
-   if (!bases || bases->next) {
-   ret = 1;
-   goto cleanup_return;
-   }
-
-   /* And the found one must be one of the reflog entries */
-   for (i = 0; i < revs.nr; i++)
-   if (>item->object == [i]->object)
-   break; /* found */
-   if (revs.nr <= i) {
-   ret = 1; /* not found */
-   goto cleanup_return;
-   }
-
-   printf("%s\n", oid_to_hex(>item->object.oid));
+   if (!fork_point)
+   return 1;
 
-cleanup_return:
-   free_commit_list(bases);
-   return ret;
+   printf("%s\n", oid_to_hex(_point->object.oid));
+   return 0;
 }
 
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
diff --git a/commit.c b/commit.c
index 30d1af2b20..a3fc77a4eb 100644
--- a/commit.c
+++ b/commit.c
@@ -17,6 +17,7 @@
 #include "sha1-lookup.h"
 #include "wt-status.h"
 #include "advice.h"
+#include "refs.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char 
*buf, size_t len, const char **);
 
@@ -958,6 +959,86 @@ static struct commit_list *merge_bases_many(struct commit 
*one, int n, struct co
return result;
 }
 
+struct rev_collect {
+   struct commit **commit;
+   int nr;
+   int alloc;
+   unsigned int initial : 1;
+};
+
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
+{
+   struct commit *commit;
+
+   if (is_null_oid(oid))
+   return;
+
+   commit = lookup_commit(the_repository, oid);
+   if (!commit ||

[PATCH 18/18] builtin rebase: support --root

2018-08-08 Thread Pratik Karki
This option allows to rebase entire histories up to, and including, the
root commit.

The conversion from the shell script is straight-forward, apart from
the fact that we do not have to write an empty tree in C.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 31 +--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 65e7be1c48..94abaaa890 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -76,6 +76,7 @@ struct rebase_options {
const char *revisions;
const char *switch_to;
int root;
+   struct object_id *squash_onto;
struct commit *restrict_revision;
int dont_finish_rebase;
enum {
@@ -375,6 +376,9 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->rebase_cousins ? "t" : "");
add_var(_snippet, "strategy", opts->strategy);
add_var(_snippet, "strategy_opts", opts->strategy_opts);
+   add_var(_snippet, "rebase_root", opts->root ? "t" : "");
+   add_var(_snippet, "squash_onto",
+   opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -653,6 +657,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
const char *rebase_merges = NULL;
int fork_point = -1;
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
+   struct object_id squash_onto;
+   char *squash_onto_name = NULL;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -743,6 +749,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("option"),
N_("pass the argument through to the merge "
   "strategy")),
+   OPT_BOOL(0, "root", ,
+N_("rebase all reachable commits up to the root(s)")),
OPT_END(),
};
 
@@ -1020,6 +1028,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
}
 
+   if (options.root && !options.onto_name)
+   imply_interactive(, "--root without --onto");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1058,8 +1069,22 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
options.upstream_arg = options.upstream_name;
-   } else
-   die("TODO: upstream for --root");
+   } else {
+   if (!options.onto_name) {
+   if (commit_tree("", 0, the_hash_algo->empty_tree, NULL,
+   _onto, NULL, NULL) < 0)
+   die(_("Could not create new root commit"));
+   options.squash_onto = _onto;
+   options.onto_name = squash_onto_name =
+   xstrdup(oid_to_hex(_onto));
+   }
+   options.upstream_name = NULL;
+   options.upstream = NULL;
+   if (argc > 1)
+   usage_with_options(builtin_rebase_usage,
+  builtin_rebase_options);
+   options.upstream_arg = "--root";
+   }
 
/* Make sure the branch to rebase onto is valid. */
if (!options.onto_name)
@@ -1207,6 +1232,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 */
if (can_fast_forward(options.onto, _head, _base) &&
!is_interactive() && !options.restrict_revision &&
+   options.upstream &&
!oidcmp(>object.oid, >object.oid)) {
int flag;
 
@@ -1311,5 +1337,6 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
free(options.head_name);
free(options.gpg_sign_opt);
free(options.cmd);
+   free(squash_onto_name);
return ret;
 }
-- 
2.18.0



[PATCH 13/18] builtin rebase: support `--allow-empty-message` option

2018-08-08 Thread Pratik Karki
This commit introduces the `--allow-empty-message` option to
`builtin/rebase.c`. The motivation behind this option is: if there are
empty messages (which is not allowed in Git by default, but can be
imported from different version control systems), the rebase will fail.

Using `--allow-empty-message` overrides that behaviour which will allow
the commits having empty messages to continue in rebase operation.

Note: a very recent change made this the default in the shell scripted
`git rebase`, therefore the builtin rebase does the same.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index c9e992b526..dfb1e6c25b 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -94,6 +94,7 @@ struct rebase_options {
char *gpg_sign_opt;
int autostash;
char *cmd;
+   int allow_empty_message;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -348,6 +349,8 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(_snippet, "cmd", opts->cmd);
+   add_var(_snippet, "allow_empty_message",
+   opts->allow_empty_message ?  "--allow-empty-message" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -598,6 +601,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.flags = REBASE_NO_QUIET,
.git_am_opt = STRBUF_INIT,
.allow_rerere_autoupdate  = -1,
+   .allow_empty_message = 1,
};
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
@@ -697,6 +701,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_STRING_LIST('x', "exec", , N_("exec"),
N_("add exec lines after each commit of the "
   "editable list")),
+   OPT_BOOL(0, "allow-empty-message",
+_empty_message,
+N_("allow rebasing commits with empty messages")),
OPT_END(),
};
 
-- 
2.18.0



[PATCH 11/18] builtin rebase: support `--autostash` option

2018-08-08 Thread Pratik Karki
To support `--autostash` we introduce a function `apply_autostash()`
just like in `git-legacy-rebase.sh`.

Rather than refactoring and using the same function that exists in
`sequencer.c`, we go a different route here, to avoid clashes with
the sister GSoC project that turns the interactive rebase into a
builtin.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 117 +++
 1 file changed, 109 insertions(+), 8 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 4437c86513..a6bfa73915 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -63,12 +63,6 @@ static int use_builtin_rebase(void)
return ret;
 }
 
-static int apply_autostash(void)
-{
-   warning("TODO");
-   return 0;
-}
-
 struct rebase_options {
enum rebase_type type;
const char *state_dir;
@@ -98,6 +92,7 @@ struct rebase_options {
int keep_empty;
int autosquash;
char *gpg_sign_opt;
+   int autostash;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -224,13 +219,56 @@ static int read_basic_state(struct rebase_options *opts)
return 0;
 }
 
+static int apply_autostash(struct rebase_options *opts)
+{
+   const char *path = state_dir_path("autostash", opts);
+   struct strbuf autostash = STRBUF_INIT;
+   struct child_process stash_apply = CHILD_PROCESS_INIT;
+
+   if (!file_exists(path))
+   return 0;
+
+   if (read_one(state_dir_path("autostash", opts), ))
+   return error(_("Could not read '%s'"), path);
+   argv_array_pushl(_apply.args,
+"stash", "apply", autostash.buf, NULL);
+   stash_apply.git_cmd = 1;
+   stash_apply.no_stderr = stash_apply.no_stdout =
+   stash_apply.no_stdin = 1;
+   if (!run_command(_apply))
+   printf("Applied autostash.\n");
+   else {
+   struct argv_array args = ARGV_ARRAY_INIT;
+   int res = 0;
+
+   argv_array_pushl(,
+"stash", "store", "-m", "autostash", "-q",
+autostash.buf, NULL);
+   if (run_command_v_opt(args.argv, RUN_GIT_CMD))
+   res = error(_("Cannot store %s"), autostash.buf);
+   argv_array_clear();
+   strbuf_release();
+   if (res)
+   return res;
+
+   fprintf(stderr,
+   _("Applying autostash resulted in conflicts.\n"
+ "Your changes are safe in the stash.\n"
+ "You can run \"git stash pop\" or \"git stash drop\" "
+ "at any time.\n"));
+   }
+
+   strbuf_release();
+   return 0;
+}
+
 static int finish_rebase(struct rebase_options *opts)
 {
struct strbuf dir = STRBUF_INIT;
const char *argv_gc_auto[] = { "gc", "--auto", NULL };
 
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-   apply_autostash();
+   apply_autostash(opts);
close_all_packs(the_repository->objects);
/*
 * We ignore errors in 'gc --auto', since the
@@ -345,7 +383,7 @@ static int run_specific_rebase(struct rebase_options *opts)
} else if (status == 2) {
struct strbuf dir = STRBUF_INIT;
 
-   apply_autostash();
+   apply_autostash(opts);
strbuf_addstr(, opts->state_dir);
remove_dir_recursively(, 0);
strbuf_release();
@@ -480,6 +518,11 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return 0;
}
 
+   if (!strcmp(var, "rebase.autostash")) {
+   opts->autostash = git_config_bool(var, value);
+   return 0;
+   }
+
return git_default_config(var, value, data);
 }
 
@@ -646,6 +689,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
N_("whitespace"), N_("passed to 'git apply'")),
OPT_SET_INT('C', 0, _c, N_("passed to 'git apply'"),
REBASE_AM),
+   OPT_BOOL(0, "autostash", ,
+N_("automatically stash/stash pop before and after")),
OPT_END(),
};
 
@@ -975,6 +1020,62 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (read_index(the_repository->index) < 0)
die(_("could not read index"));
 
+   if (options.autostash) {
+   struct lock_file lock_file = LOCK_INIT;
+   int fd;
+
+   fd = hold_locked_index(_file, 0)

[PATCH 12/18] builtin rebase: support `--exec`

2018-08-08 Thread Pratik Karki
This commit adds support for the `--exec` option which takes a shell
command-line as argument. This argument will be appended as an `exec
` command after each line in the todo list that creates a commit in
the final history.  commands.

Note: while the shell script version of `git rebase` assigned the empty
string to `cmd` by default, we *unset* it here because the code looks
nicer and it does not change the behavior.

The `--exec` option requires `--interactive` machinery.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index a6bfa73915..c9e992b526 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -93,6 +93,7 @@ struct rebase_options {
int autosquash;
char *gpg_sign_opt;
int autostash;
+   char *cmd;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -346,6 +347,7 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
+   add_var(_snippet, "cmd", opts->cmd);
 
switch (opts->type) {
case REBASE_AM:
@@ -619,6 +621,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
const char *gpg_sign = NULL;
int opt_c = -1;
struct string_list whitespace = STRING_LIST_INIT_NODUP;
+   struct string_list exec = STRING_LIST_INIT_NODUP;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -691,6 +694,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
REBASE_AM),
OPT_BOOL(0, "autostash", ,
 N_("automatically stash/stash pop before and after")),
+   OPT_STRING_LIST('x', "exec", , N_("exec"),
+   N_("add exec lines after each commit of the "
+  "editable list")),
OPT_END(),
};
 
@@ -915,6 +921,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
}
 
+   if (exec.nr) {
+   int i;
+
+   imply_interactive(, "--exec");
+
+   strbuf_reset();
+   for (i = 0; i < exec.nr; i++)
+   strbuf_addf(, "exec %s\n", exec.items[i].string);
+   options.cmd = xstrdup(buf.buf);
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1197,5 +1214,6 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
strbuf_release();
free(options.head_name);
free(options.gpg_sign_opt);
+   free(options.cmd);
return ret;
 }
-- 
2.18.0



[PATCH 09/18] builtin rebase: support `--gpg-sign` option

2018-08-08 Thread Pratik Karki
This commit introduces support for `--gpg-sign` option which is used
to GPG-sign commits.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 27 +++
 1 file changed, 27 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 79ba65fd75..cd9caf4841 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -97,6 +97,7 @@ struct rebase_options {
int allow_rerere_autoupdate;
int keep_empty;
int autosquash;
+   char *gpg_sign_opt;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -209,6 +210,15 @@ static int read_basic_state(struct rebase_options *opts)
} else
opts->allow_rerere_autoupdate = -1;
 
+   if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("gpg_sign_opt", opts),
+   ))
+   return -1;
+   free(opts->gpg_sign_opt);
+   opts->gpg_sign_opt = xstrdup(buf.buf);
+   }
+
strbuf_release();
 
return 0;
@@ -297,6 +307,7 @@ static int run_specific_rebase(struct rebase_options *opts)
"--rerere-autoupdate" : "--no-rerere-autoupdate");
add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
+   add_var(_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
 
switch (opts->type) {
case REBASE_AM:
@@ -462,6 +473,13 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return 0;
}
 
+   if (!strcmp(var, "commit.gpgsign")) {
+   free(opts->gpg_sign_opt);
+   opts->gpg_sign_opt = git_config_bool(var, value) ?
+   xstrdup("-S") : NULL;
+   return 0;
+   }
+
return git_default_config(var, value, data);
 }
 
@@ -555,6 +573,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int committer_date_is_author_date = 0;
int ignore_date = 0;
int ignore_whitespace = 0;
+   const char *gpg_sign = NULL;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -619,6 +638,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_BOOL(0, "autosquash", ,
 N_("move commits that begin with "
"squash!/fixup! under -i")),
+   OPT_STRING('S', "gpg-sign", _sign,
+  N_("gpg-sign?"), N_("GPG-sign commits")),
OPT_END(),
};
 
@@ -821,6 +842,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (options.keep_empty)
imply_interactive(, "--keep-empty");
 
+   if (gpg_sign) {
+   free(options.gpg_sign_opt);
+   options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1046,5 +1072,6 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 cleanup:
strbuf_release();
free(options.head_name);
+   free(options.gpg_sign_opt);
return ret;
 }
-- 
2.18.0



[PATCH 10/18] builtin rebase: support `-C` and `--whitespace=`

2018-08-08 Thread Pratik Karki
This commit converts more code from the shell script version to the
builtin rebase. In this instance, we just have to be careful to
keep support for passing multiple `--whitespace` options, as the
shell script version does so, too.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 23 +++
 1 file changed, 23 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index cd9caf4841..4437c86513 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -574,6 +574,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int ignore_date = 0;
int ignore_whitespace = 0;
const char *gpg_sign = NULL;
+   int opt_c = -1;
+   struct string_list whitespace = STRING_LIST_INIT_NODUP;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -640,6 +642,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
"squash!/fixup! under -i")),
OPT_STRING('S', "gpg-sign", _sign,
   N_("gpg-sign?"), N_("GPG-sign commits")),
+   OPT_STRING_LIST(0, "whitespace", ,
+   N_("whitespace"), N_("passed to 'git apply'")),
+   OPT_SET_INT('C', 0, _c, N_("passed to 'git apply'"),
+   REBASE_AM),
OPT_END(),
};
 
@@ -847,6 +853,23 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
}
 
+   if (opt_c >= 0)
+   strbuf_addf(_am_opt, " -C%d", opt_c);
+
+   if (whitespace.nr) {
+   int i;
+
+   for (i = 0; i < whitespace.nr; i++) {
+   const char *item = whitespace.items[i].string;
+
+   strbuf_addf(_am_opt, " --whitespace=%s",
+   item);
+
+   if ((!strcmp(item, "fix")) || (!strcmp(item, "strip")))
+   options.flags |= REBASE_FORCE;
+   }
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 08/18] builtin rebase: support `--autosquash`

2018-08-08 Thread Pratik Karki
This commit adds support for the `--autosquash` option which is used to
automatically squash the commits marked as `squash` or `fixup` in their
messages. This is converted following `git-legacy-rebase.sh` closely.

This option can also be configured via the Git config setting
rebase.autosquash. To support this, we also add a custom
rebase_config() function in this commit that will be used instead (and
falls back to) git_default_config().

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fd9ad8efae..79ba65fd75 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -96,6 +96,7 @@ struct rebase_options {
int signoff;
int allow_rerere_autoupdate;
int keep_empty;
+   int autosquash;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -295,6 +296,7 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->allow_rerere_autoupdate ?
"--rerere-autoupdate" : "--no-rerere-autoupdate");
add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+   add_var(_snippet, "autosquash", opts->autosquash ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -455,6 +457,11 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return 0;
}
 
+   if (!strcmp(var, "rebase.autosquash")) {
+   opts->autosquash = git_config_bool(var, value);
+   return 0;
+   }
+
return git_default_config(var, value, data);
 }
 
@@ -609,6 +616,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
"conflict")),
OPT_BOOL(0, "keep-empty", _empty,
 N_("preserve empty commits during rebase")),
+   OPT_BOOL(0, "autosquash", ,
+N_("move commits that begin with "
+   "squash!/fixup! under -i")),
OPT_END(),
};
 
-- 
2.18.0



[PATCH 07/18] builtin rebase: support `keep-empty` option

2018-08-08 Thread Pratik Karki
The `--keep-empty` option can be used to keep the commits that do not
change anything from its parents in the result.

While the scripted version uses `interactive_rebase=implied` to indicate
that the rebase needs to use the `git-rebase--interactive` backend in
non-interactive mode as fallback when figuring out which backend to use,
the C version needs to use a different route because the backend will
already be chosen during the `parse_options()` call.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 24 
 1 file changed, 24 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 42ee040da3..fd9ad8efae 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -95,6 +95,7 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
+   int keep_empty;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -103,6 +104,23 @@ static int is_interactive(struct rebase_options *opts)
opts->type == REBASE_PRESERVE_MERGES;
 }
 
+static void imply_interactive(struct rebase_options *opts, const char *option)
+{
+   switch (opts->type) {
+   case REBASE_AM:
+   die(_("%s requires an interactive rebase"), option);
+   break;
+   case REBASE_INTERACTIVE:
+   case REBASE_PRESERVE_MERGES:
+   break;
+   case REBASE_MERGE:
+   /* we silently *upgrade* --merge to --interactive if needed */
+   default:
+   opts->type = REBASE_INTERACTIVE; /* implied */
+   break;
+   }
+}
+
 /* Returns the filename prefixed by the state_dir */
 static const char *state_dir_path(const char *filename, struct rebase_options 
*opts)
 {
@@ -276,6 +294,7 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->allow_rerere_autoupdate < 0 ? "" :
opts->allow_rerere_autoupdate ?
"--rerere-autoupdate" : "--no-rerere-autoupdate");
+   add_var(_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -588,6 +607,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 _rerere_autoupdate,
 N_("allow rerere to update index  with resolved "
"conflict")),
+   OPT_BOOL(0, "keep-empty", _empty,
+N_("preserve empty commits during rebase")),
OPT_END(),
};
 
@@ -787,6 +808,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.flags |= REBASE_FORCE;
}
 
+   if (options.keep_empty)
+   imply_interactive(, "--keep-empty");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 05/18] builtin rebase: support `ignore-whitespace` option

2018-08-08 Thread Pratik Karki
This commit adds support for the `--ignore-whitespace` option
of the rebase command. This option is simply passed to the
`--am` backend.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index eef16206c2..7490d215ef 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -527,6 +527,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
int committer_date_is_author_date = 0;
+   int ignore_whitespace = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -542,6 +543,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
{OPTION_NEGBIT, 'n', "no-stat", , NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+   OPT_BOOL(0, "ignore-whitespace", _whitespace,
+N_("passed to 'git apply'")),
OPT_BOOL(0, "signoff", ,
 N_("add a Signed-off-by: line to each commit")),
OPT_BOOL(0, "committer-date-is-author-date",
@@ -773,6 +776,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.flags |= REBASE_FORCE;
}
 
+   if (ignore_whitespace)
+   strbuf_addstr(_am_opt, " --ignore-whitespace");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 04/18] builtin rebase: support --committer-date-is-author-date

2018-08-08 Thread Pratik Karki
This option is simply handed down to `git am` by way of setting the
`git_am_opt` variable that is handled by the `git-rebase--am` backend.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 1729d2d9e2..eef16206c2 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -526,6 +526,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_EDIT_TODO,
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
+   int committer_date_is_author_date = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -543,6 +544,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
OPT_BOOL(0, "signoff", ,
 N_("add a Signed-off-by: line to each commit")),
+   OPT_BOOL(0, "committer-date-is-author-date",
+_date_is_author_date,
+N_("passed to 'git am'")),
OPT_BIT('f', "force-rebase", ,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
@@ -763,6 +767,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (!(options.flags & REBASE_NO_QUIET))
strbuf_addstr(_am_opt, " -q");
 
+   if (committer_date_is_author_date) {
+   strbuf_addstr(_am_opt,
+ " --committer-date-is-author-date");
+   options.flags |= REBASE_FORCE;
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[PATCH 02/18] builtin rebase: support --signoff

2018-08-08 Thread Pratik Karki
This commit adds support for `--signoff` which is used to add a
`Signed-off-by` trailer to all the rebased commits. The actual
handling is left to the rebase backends.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 17 +
 1 file changed, 17 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fc9b5a8a60..a491481120 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -93,6 +93,7 @@ struct rebase_options {
} flags;
struct strbuf git_am_opt;
const char *action;
+   int signoff;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -168,6 +169,11 @@ static int read_basic_state(struct rebase_options *opts)
if (file_exists(state_dir_path("verbose", opts)))
opts->flags |= REBASE_VERBOSE;
 
+   if (file_exists(state_dir_path("signoff", opts))) {
+   opts->signoff = 1;
+   opts->flags |= REBASE_FORCE;
+   }
+
strbuf_release();
 
return 0;
@@ -249,6 +255,7 @@ static int run_specific_rebase(struct rebase_options *opts)
if (opts->switch_to)
add_var(_snippet, "switch_to", opts->switch_to);
add_var(_snippet, "action", opts->action ? opts->action : "");
+   add_var(_snippet, "signoff", opts->signoff ? "--signoff" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -513,6 +520,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
{OPTION_NEGBIT, 'n', "no-stat", , NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+   OPT_BOOL(0, "signoff", ,
+N_("add a Signed-off-by: line to each commit")),
OPT_BIT('f', "force-rebase", ,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
@@ -745,6 +754,14 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
break;
}
 
+   if (options.signoff) {
+   if (options.type == REBASE_PRESERVE_MERGES)
+   die("cannot combine '--signoff' with "
+   "'--preserve-merges'");
+   strbuf_addstr(_am_opt, " --signoff");
+   options.flags |= REBASE_FORCE;
+   }
+
if (!options.root) {
if (argc < 1)
die("TODO: handle @{upstream}");
-- 
2.18.0



[PATCH 01/18] builtin rebase: allow selecting the rebase "backend"

2018-08-08 Thread Pratik Karki
With this commit the builtin rebase supports selecting the "rebase
backends" (or "type") `interactive`, `preserve-merges`, and `merge`.

The `state_dir` was already handled according to the rebase type in a
previous commit.

Note that there is one quirk in the shell script: `--interactive`
followed by `--merge` won't reset the type to "merge" but keeps the type
as "interactive". And as t3418 tests this explicitly, we have to support
it in the builtin rebase, too.

Likewise, `--interactive` followed by `--preserve-merges` makes it an
"explicitly interactive" rebase, i.e. a rebase that should show the todo
list, while `--preserve-merges` alone is not interactive (and t5520
tests for this via `git pull --rebase=preserve`).

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 37 +
 1 file changed, 37 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 75c4ac66e0..fc9b5a8a60 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -452,6 +452,29 @@ static int can_fast_forward(struct commit *onto, struct 
object_id *head_oid,
return res && is_linear_history(onto, head);
 }
 
+/* -i followed by -m is still -i */
+static int parse_opt_merge(const struct option *opt, const char *arg, int 
unset)
+{
+   struct rebase_options *opts = opt->value;
+
+   if (!is_interactive(opts))
+   opts->type = REBASE_MERGE;
+
+   return 0;
+}
+
+/* -i followed by -p is still explicitly interactive, but -p alone is not */
+static int parse_opt_interactive(const struct option *opt, const char *arg,
+int unset)
+{
+   struct rebase_options *opts = opt->value;
+
+   opts->type = REBASE_INTERACTIVE;
+   opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
+
+   return 0;
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -510,6 +533,17 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_CMDMODE(0, "show-current-patch", ,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
+   { OPTION_CALLBACK, 'm', "merge", , NULL,
+   N_("use merging strategies to rebase"),
+   PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+   parse_opt_merge },
+   { OPTION_CALLBACK, 'i', "interactive", , NULL,
+   N_("let the user edit the list of commits to rebase"),
+   PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+   parse_opt_interactive },
+   OPT_SET_INT('p', "preserve-merges", ,
+   N_("try to recreate merges instead of ignoring "
+  "them"), REBASE_PRESERVE_MERGES),
OPT_END(),
};
 
@@ -884,6 +918,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
diff_flush();
}
 
+   if (is_interactive())
+   goto run_rebase;
+
/* Detach HEAD and reset the tree */
if (options.flags & REBASE_NO_QUIET)
printf(_("First, rewinding head to replay your work on top of "
-- 
2.18.0



[PATCH 03/18] builtin rebase: support --rerere-autoupdate

2018-08-08 Thread Pratik Karki
The `--rerere-autoupdate` option allows rerere to update the index with
resolved conflicts. This commit follows closely the equivalent part of
`git-legacy-rebase.sh`.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 25 +
 1 file changed, 25 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index a491481120..1729d2d9e2 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -94,6 +94,7 @@ struct rebase_options {
struct strbuf git_am_opt;
const char *action;
int signoff;
+   int allow_rerere_autoupdate;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -174,6 +175,21 @@ static int read_basic_state(struct rebase_options *opts)
opts->flags |= REBASE_FORCE;
}
 
+   if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
+   strbuf_reset();
+   if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
+   ))
+   return -1;
+   if (!strcmp(buf.buf, "--rerere-autoupdate"))
+   opts->allow_rerere_autoupdate = 1;
+   else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
+   opts->allow_rerere_autoupdate = 0;
+   else
+   warning(_("ignoring invalid allow_rerere_autoupdate: "
+ "'%s'"), buf.buf);
+   } else
+   opts->allow_rerere_autoupdate = -1;
+
strbuf_release();
 
return 0;
@@ -256,6 +272,10 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "switch_to", opts->switch_to);
add_var(_snippet, "action", opts->action ? opts->action : "");
add_var(_snippet, "signoff", opts->signoff ? "--signoff" : "");
+   add_var(_snippet, "allow_rerere_autoupdate",
+   opts->allow_rerere_autoupdate < 0 ? "" :
+   opts->allow_rerere_autoupdate ?
+   "--rerere-autoupdate" : "--no-rerere-autoupdate");
 
switch (opts->type) {
case REBASE_AM:
@@ -488,6 +508,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.type = REBASE_UNSPECIFIED,
.flags = REBASE_NO_QUIET,
.git_am_opt = STRBUF_INIT,
+   .allow_rerere_autoupdate  = -1,
};
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
@@ -553,6 +574,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_SET_INT('p', "preserve-merges", ,
N_("try to recreate merges instead of ignoring "
   "them"), REBASE_PRESERVE_MERGES),
+   OPT_BOOL(0, "rerere-autoupdate",
+_rerere_autoupdate,
+N_("allow rerere to update index  with resolved "
+   "conflict")),
OPT_END(),
};
 
-- 
2.18.0



[PATCH 06/18] builtin rebase: support `ignore-date` option

2018-08-08 Thread Pratik Karki
This commit adds support for `--ignore-date` which is passed to `git am`
to easily change the dates of the rebased commits.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 7490d215ef..42ee040da3 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -527,6 +527,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
int committer_date_is_author_date = 0;
+   int ignore_date = 0;
int ignore_whitespace = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -550,6 +551,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_BOOL(0, "committer-date-is-author-date",
 _date_is_author_date,
 N_("passed to 'git am'")),
+   OPT_BOOL(0, "ignore-date", _date,
+N_("passed to 'git am'")),
OPT_BIT('f', "force-rebase", ,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
@@ -779,6 +782,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (ignore_whitespace)
strbuf_addstr(_am_opt, " --ignore-whitespace");
 
+   if (ignore_date) {
+   strbuf_addstr(_am_opt, " --ignore-date");
+   options.flags |= REBASE_FORCE;
+   }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[GSoC] [PATCH 00/18] builtin rebase options

2018-08-08 Thread Pratik Karki
This patch series completes the support for all rebase options in the
builtin rebase. This converts the remaining command-line options.

The previous patch series taught the builtin rebase to handle different
actions, this patch series will continue adding functionality to
builtin rebase by teaching it to handle options handled by original rebase
i.e. `--signoff`, `rerere-autoupdate`, et al.

It adds the functionality to the previous patch series,
(https://public-inbox.org/git/20180808150622.9614-1-predatoram...@gmail.com).

This is the fourth patch series that brings us more closer to a builtin
"git rebase".

If you like to view the development branch, you can view
(https://github.com/git/git/pull/505), where I have kept my commits up to date
and leveraged Travis(there is sporadic failures in t5520 for macos gcc and
isn't due to my patches) for extra testing other than my system.

The next in line patch series which I'll be sending out today are:

builtin rebase rest: The builtin rebase will convert all the remaining shell
scripts from the original rebase to C.
default to builtin rebase: This will turn on the feature-complete builtin
rebase to on.

These patch series are built on top of each other, i.e. they depend on this
order.

Pratik Karki (18):
  builtin rebase: allow selecting the rebase "backend"
  builtin rebase: support --signoff
  builtin rebase: support --rerere-autoupdate
  builtin rebase: support --committer-date-is-author-date
  builtin rebase: support `ignore-whitespace` option
  builtin rebase: support `ignore-date` option
  builtin rebase: support `keep-empty` option
  builtin rebase: support `--autosquash`
  builtin rebase: support `--gpg-sign` option
  builtin rebase: support `-C` and `--whitespace=`
  builtin rebase: support `--autostash` option
  builtin rebase: support `--exec`
  builtin rebase: support `--allow-empty-message` option
  builtin rebase: support --rebase-merges[=[no-]rebase-cousins]
  merge-base --fork-point: extract libified function
  builtin rebase: support `fork-point` option
  builtin rebase: add support for custom merge strategies
  builtin rebase: support --root

 builtin/merge-base.c |  81 +---
 builtin/rebase.c | 449 ++-
 commit.c |  81 
 commit.h |   2 +
 4 files changed, 528 insertions(+), 85 deletions(-)

-- 
2.18.0



[PATCH 7/7] builtin rebase: stop if `git am` is in progress

2018-08-08 Thread Pratik Karki
This commit checks for the file `applying` used by `git am` in
`rebase-apply/` and if the file is present it means `git am` is in
progress so it errors out.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 1344e071f3..75c4ac66e0 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -539,6 +539,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 
git_config(rebase_config, );
 
+   strbuf_reset();
+   strbuf_addf(, "%s/applying", apply_dir());
+   if(file_exists(buf.buf))
+   die(_("It looks like 'git am' is in progress. Cannot rebase."));
+
if (is_directory(apply_dir())) {
options.type = REBASE_AM;
options.state_dir = apply_dir();
-- 
2.18.0



[PATCH 6/7] builtin rebase: actions require a rebase in progress

2018-08-08 Thread Pratik Karki
This commit prevents actions (such as --continue, --skip) from running
when there is no rebase in progress.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index e3dd2f511e..1344e071f3 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -577,6 +577,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_rebase_usage,
   builtin_rebase_options);
 
+   if (action != NO_ACTION && !in_progress)
+   die(_("No rebase in progress?"));
+
if (action == ACTION_EDIT_TODO && !is_interactive())
die(_("The --edit-todo action can only be used during "
  "interactive rebase."));
-- 
2.18.0



[PATCH 5/7] builtin rebase: support --edit-todo and --show-current-patch

2018-08-08 Thread Pratik Karki
While these sub-commands are very different in spirit, their
implementation is almost identical, so we convert them in one go.

And since those are the last sub-commands that needed to be converted,
now we can also turn that `default:` case into a bug (because we should
now handle all the actions).

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 23 ++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 0d805480a1..e3dd2f511e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -472,6 +472,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_SKIP,
ACTION_ABORT,
ACTION_QUIT,
+   ACTION_EDIT_TODO,
+   ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -503,6 +505,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_ABORT),
OPT_CMDMODE(0, "quit", ,
N_("abort but keep HEAD where it is"), ACTION_QUIT),
+   OPT_CMDMODE(0, "edit-todo", , N_("edit the todo list "
+   "during an interactive rebase"), ACTION_EDIT_TODO),
+   OPT_CMDMODE(0, "show-current-patch", ,
+   N_("show the patch file being applied or merged"),
+   ACTION_SHOW_CURRENT_PATCH),
OPT_END(),
};
 
@@ -570,6 +577,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_rebase_usage,
   builtin_rebase_options);
 
+   if (action == ACTION_EDIT_TODO && !is_interactive())
+   die(_("The --edit-todo action can only be used during "
+ "interactive rebase."));
+
switch (action) {
case ACTION_CONTINUE: {
struct object_id head;
@@ -639,8 +650,18 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("could not remove '%s'"), options.state_dir);
goto cleanup;
}
+   case ACTION_EDIT_TODO:
+   options.action = "edit-todo";
+   options.dont_finish_rebase = 1;
+   goto run_rebase;
+   case ACTION_SHOW_CURRENT_PATCH:
+   options.action = "show-current-patch";
+   options.dont_finish_rebase = 1;
+   goto run_rebase;
+   case NO_ACTION:
+   break;
default:
-   die("TODO");
+   BUG("action: %d", action);
}
 
/* Make sure no rebase is in progress */
-- 
2.18.0



[PATCH 1/7] builtin rebase: support --continue

2018-08-08 Thread Pratik Karki
This commit adds the option `--continue` which is used to resume
rebase after merge conflicts. The code tries to stay as close to
the equivalent shell scripts found in `git-legacy-rebase.sh` as
possible.

When continuing a rebase, the state variables are read from state_dir.
Some of the state variables are not actually stored there, such as
`upstream`. The shell script version simply does not set them, but for
consistency, we unset them in the builtin version.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 115 +--
 strbuf.c |   9 
 strbuf.h |   3 ++
 3 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index b2ddfa8dbf..10da4c978b 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -91,6 +91,7 @@ struct rebase_options {
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
struct strbuf git_am_opt;
+   const char *action;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -115,6 +116,62 @@ static const char *state_dir_path(const char *filename, 
struct rebase_options *o
return path.buf;
 }
 
+/* Read one file, then strip line endings */
+static int read_one(const char *path, struct strbuf *buf)
+{
+   if (strbuf_read_file(buf, path, 0) < 0)
+   return error_errno(_("could not read '%s'"), path);
+   strbuf_trim_trailing_newline(buf);
+   return 0;
+}
+
+/* Initialize the rebase options from the state directory. */
+static int read_basic_state(struct rebase_options *opts)
+{
+   struct strbuf head_name = STRBUF_INIT;
+   struct strbuf buf = STRBUF_INIT;
+   struct object_id oid;
+
+   if (read_one(state_dir_path("head-name", opts), _name) ||
+   read_one(state_dir_path("onto", opts), ))
+   return -1;
+   opts->head_name = starts_with(head_name.buf, "refs/") ?
+   xstrdup(head_name.buf) : NULL;
+   strbuf_release(_name);
+   if (get_oid(buf.buf, ))
+   return error(_("could not get 'onto': '%s'"), buf.buf);
+   opts->onto = lookup_commit_or_die(, buf.buf);
+
+   /*
+* 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.
+*/
+   strbuf_reset();
+   if (file_exists(state_dir_path("orig-head", opts))) {
+   if (read_one(state_dir_path("orig-head", opts), ))
+   return -1;
+   } else if (read_one(state_dir_path("head", opts), ))
+   return -1;
+   if (get_oid(buf.buf, >orig_head))
+   return error(_("invalid orig-head: '%s'"), buf.buf);
+
+   strbuf_reset();
+   if (read_one(state_dir_path("quiet", opts), ))
+   return -1;
+   if (buf.len)
+   opts->flags &= ~REBASE_NO_QUIET;
+   else
+   opts->flags |= REBASE_NO_QUIET;
+
+   if (file_exists(state_dir_path("verbose", opts)))
+   opts->flags |= REBASE_VERBOSE;
+
+   strbuf_release();
+
+   return 0;
+}
+
 static int finish_rebase(struct rebase_options *opts)
 {
struct strbuf dir = STRBUF_INIT;
@@ -168,12 +225,13 @@ static int run_specific_rebase(struct rebase_options 
*opts)
add_var(_snippet, "state_dir", opts->state_dir);
 
add_var(_snippet, "upstream_name", opts->upstream_name);
-   add_var(_snippet, "upstream",
-oid_to_hex(>upstream->object.oid));
+   add_var(_snippet, "upstream", opts->upstream ?
+   oid_to_hex(>upstream->object.oid) : NULL);
add_var(_snippet, "head_name",
opts->head_name ? opts->head_name : "detached HEAD");
add_var(_snippet, "orig_head", oid_to_hex(>orig_head));
-   add_var(_snippet, "onto", oid_to_hex(>onto->object.oid));
+   add_var(_snippet, "onto", opts->onto ?
+   oid_to_hex(>onto->object.oid) : NULL);
add_var(_snippet, "onto_name", opts->onto_name);
add_var(_snippet, "revisions", opts->revisions);
add_var(_snippet, "restrict_revision", opts->restrict_revision ?
@@ -189,6 +247,7 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->flags & REBASE_FORCE ? "t" : "");
if (opts->switch_to)
add_var(_snippet, "switch_to", opts->switch_to);
+   add_var(_snippet, "action", opts->action ? opts->action : "");
 
switch (opts->type) {
case REB

[GSoC] [PATCH 0/7] builtin rebase actions

2018-08-08 Thread Pratik Karki
The previous patch series implemented essential options and neccessary
configurations. This patch series teaches all the rebase actions like
`--continue`, `--skip`, `--abort`, et al. to builtin rebase.

These actions are important to operation of `git rebase` hence, are kept
in a patch series of their own.

It adds the rebase actions to the previous patch series,
(https://public-inbox.org/git/20180808134830.19949-1-predatoram...@gmail.com/).

This is the third patch series that brings us more closer to a builtin
"git rebase".

If you like to view the development branch, you can view
(https://github.com/git/git/pull/505), where I have kept my commits up to date
and leveraged Travis(there is sporadic failures in t5520 for macos gcc and
isn't due to my patches) for extra testing other than my system.

The next in line patch series which I'll be sending out today are:

builtin rebase options: The builtin rebase will add all the options supported
by original rebase.
builtin rebase rest: The builtin rebase will convert all the remaining shell
scripts from the original rebase to C.
default to builtin rebase: This will turn on the feature-complete builtin
rebase to on.

These patch series are built on top of each other, i.e. they depend on this
order.

Pratik Karki (7):
  builtin rebase: support --continue
  builtin rebase: support --skip
  builtin rebase: support --abort
  builtin rebase: support --quit
  builtin rebase: support --edit-todo and --show-current-patch
  builtin rebase: actions require a rebase in progress
  builtin rebase: stop if `git am` is in progress

 builtin/rebase.c | 193 ++-
 strbuf.c |   9 +++
 strbuf.h |   3 +
 3 files changed, 201 insertions(+), 4 deletions(-)

-- 
2.18.0



[PATCH 3/7] builtin rebase: support --abort

2018-08-08 Thread Pratik Karki
This commit teaches the builtin rebase the "abort" action, which a user
can call to roll back a rebase that is in progress.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 20 
 1 file changed, 20 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 7a903838b1..8e94f6cc85 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -470,6 +470,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
NO_ACTION,
ACTION_CONTINUE,
ACTION_SKIP,
+   ACTION_ABORT,
} action = NO_ACTION;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -496,6 +497,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_CONTINUE),
OPT_CMDMODE(0, "skip", ,
N_("skip current patch and continue"), ACTION_SKIP),
+   OPT_CMDMODE(0, "abort", ,
+   N_("abort and check out the original branch"),
+   ACTION_ABORT),
OPT_END(),
};
 
@@ -608,6 +612,22 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
exit(1);
goto run_rebase;
}
+   case ACTION_ABORT: {
+   struct string_list merge_rr = STRING_LIST_INIT_DUP;
+   options.action = "abort";
+
+   rerere_clear(_rr);
+   string_list_clear(_rr, 1);
+
+   if (read_basic_state())
+   exit(1);
+   if (reset_head(_head, "reset",
+  options.head_name, 0) < 0)
+   die(_("could not move back to %s"),
+   oid_to_hex(_head));
+   ret = finish_rebase();
+   goto cleanup;
+   }
default:
die("TODO");
}
-- 
2.18.0



[PATCH 2/7] builtin rebase: support --skip

2018-08-08 Thread Pratik Karki
This commit adds the option `--skip` which is used to restart
rebase after skipping the current patch.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 10da4c978b..7a903838b1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -21,6 +21,7 @@
 #include "diff.h"
 #include "wt-status.h"
 #include "revision.h"
+#include "rerere.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -468,6 +469,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
enum {
NO_ACTION,
ACTION_CONTINUE,
+   ACTION_SKIP,
} action = NO_ACTION;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -492,6 +494,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
REBASE_FORCE),
OPT_CMDMODE(0, "continue", , N_("continue"),
ACTION_CONTINUE),
+   OPT_CMDMODE(0, "skip", ,
+   N_("skip current patch and continue"), ACTION_SKIP),
OPT_END(),
};
 
@@ -590,6 +594,20 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
exit(1);
goto run_rebase;
}
+   case ACTION_SKIP: {
+   struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+   options.action = "skip";
+
+   rerere_clear(_rr);
+   string_list_clear(_rr, 1);
+
+   if (reset_head(NULL, "reset", NULL, 0) < 0)
+   die(_("could not discard worktree changes"));
+   if (read_basic_state())
+   exit(1);
+   goto run_rebase;
+   }
default:
die("TODO");
}
-- 
2.18.0



[PATCH 4/7] builtin rebase: support --quit

2018-08-08 Thread Pratik Karki
With this patch, the builtin rebase handles the `--quit` action which
can be used to abort a rebase without rolling back any changes performed
during the rebase (this is useful when a user forgot that they were in
the middle of a rebase and continued working normally).

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 8e94f6cc85..0d805480a1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -471,6 +471,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ACTION_CONTINUE,
ACTION_SKIP,
ACTION_ABORT,
+   ACTION_QUIT,
} action = NO_ACTION;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -500,6 +501,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_CMDMODE(0, "abort", ,
N_("abort and check out the original branch"),
ACTION_ABORT),
+   OPT_CMDMODE(0, "quit", ,
+   N_("abort but keep HEAD where it is"), ACTION_QUIT),
OPT_END(),
};
 
@@ -628,6 +631,14 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
ret = finish_rebase();
goto cleanup;
}
+   case ACTION_QUIT: {
+   strbuf_reset();
+   strbuf_addstr(, options.state_dir);
+   ret = !!remove_dir_recursively(, 0);
+   if (ret)
+   die(_("could not remove '%s'"), options.state_dir);
+   goto cleanup;
+   }
default:
die("TODO");
}
-- 
2.18.0



[PATCH 10/11] builtin rebase: only store fully-qualified refs in `options.head_name`

2018-08-08 Thread Pratik Karki
When running a rebase on a detached HEAD, we currently store the string
"detached HEAD" in options.head_name. That is a faithful translation of
the shell script version, and we still kind of need it for the purposes of
the scripted backends.

It is poor style for C, though, where we would really only want a valid,
fully-qualified ref name as value, and NULL for detached HEADs, using
"detached HEAD" for display only. Make it so.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 11 ---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index a261f552f1..63634210c0 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -169,7 +169,8 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "upstream_name", opts->upstream_name);
add_var(_snippet, "upstream",
 oid_to_hex(>upstream->object.oid));
-   add_var(_snippet, "head_name", opts->head_name);
+   add_var(_snippet, "head_name",
+   opts->head_name ? opts->head_name : "detached HEAD");
add_var(_snippet, "orig_head", oid_to_hex(>orig_head));
add_var(_snippet, "onto", oid_to_hex(>onto->object.oid));
add_var(_snippet, "onto_name", opts->onto_name);
@@ -251,6 +252,9 @@ static int reset_head(struct object_id *oid, const char 
*action,
*old_orig = NULL, oid_old_orig;
int ret = 0;
 
+   if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+   BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
return -1;
 
@@ -558,7 +562,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 * branch_name -- branch/commit being rebased, or
 *HEAD (already detached)
 * orig_head -- commit object name of tip of the branch before rebasing
-* head_name -- refs/heads/ or "detached HEAD"
+* head_name -- refs/heads/ or NULL (detached HEAD)
 */
if (argc > 0)
 die("TODO: handle switch_to");
@@ -575,7 +579,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
branch_name = options.head_name;
 
} else {
-   options.head_name = xstrdup("detached HEAD");
+   free(options.head_name);
+   options.head_name = NULL;
branch_name = "HEAD";
}
if (get_oid("HEAD", _head))
-- 
2.18.0



[PATCH 09/11] builtin rebase: start a new rebase only if none is in progress

2018-08-08 Thread Pratik Karki
To run a new rebase, there needs to be a check to assure that no other
rebase is in progress. New rebase operation cannot start until an
ongoing rebase operation completes or is terminated.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 48 +++-
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 8a7bf3d468..a261f552f1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -87,6 +87,7 @@ struct rebase_options {
REBASE_VERBOSE = 1<<1,
REBASE_DIFFSTAT = 1<<2,
REBASE_FORCE = 1<<3,
+   REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
struct strbuf git_am_opt;
 };
@@ -392,10 +393,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
.git_am_opt = STRBUF_INIT,
};
const char *branch_name;
-   int ret, flags;
+   int ret, flags, in_progress = 0;
int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
+   struct strbuf buf = STRBUF_INIT;
struct object_id merge_base;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
@@ -447,6 +449,30 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 
git_config(rebase_config, );
 
+   if (is_directory(apply_dir())) {
+   options.type = REBASE_AM;
+   options.state_dir = apply_dir();
+   } else if (is_directory(merge_dir())) {
+   strbuf_reset();
+   strbuf_addf(, "%s/rewritten", merge_dir());
+   if (is_directory(buf.buf)) {
+   options.type = REBASE_PRESERVE_MERGES;
+   options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+   } else {
+   strbuf_reset();
+   strbuf_addf(, "%s/interactive", merge_dir());
+   if(file_exists(buf.buf)) {
+   options.type = REBASE_INTERACTIVE;
+   options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+   } else
+   options.type = REBASE_MERGE;
+   }
+   options.state_dir = merge_dir();
+   }
+
+   if (options.type != REBASE_UNSPECIFIED)
+   in_progress = 1;
+
argc = parse_options(argc, argv, prefix,
 builtin_rebase_options,
 builtin_rebase_usage, 0);
@@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_rebase_usage,
   builtin_rebase_options);
 
+   /* Make sure no rebase is in progress */
+   if (in_progress) {
+   const char *last_slash = strrchr(options.state_dir, '/');
+   const char *state_dir_base =
+   last_slash ? last_slash + 1 : options.state_dir;
+   const char *cmd_live_rebase =
+   "git rebase (--continue | --abort | --skip)";
+   strbuf_reset();
+   strbuf_addf(, "rm -fr \"%s\"", options.state_dir);
+   die(_("It seems that there is already a %s directory, and\n"
+ "I wonder if you are in the middle of another rebase.  "
+ "If that is the\n"
+ "case, please try\n\t%s\n"
+ "If that is not the case, please\n\t%s\n"
+ "and run me again.  I am stopping in case you still "
+ "have something\n"
+ "valuable there.\n"),
+   state_dir_base, cmd_live_rebase,buf.buf);
+   }
+
if (!(options.flags & REBASE_NO_QUIET))
strbuf_addstr(_am_opt, " -q");
 
-- 
2.18.0



[PATCH 11/11] builtin rebase: support `git rebase `

2018-08-08 Thread Pratik Karki
This commit adds support for `switch-to` which is used to switch to the
target branch if needed. The equivalent codes found in shell script
`git-legacy-rebase.sh` is converted to builtin `rebase.c`.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 48 
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 63634210c0..b2ddfa8dbf 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,7 @@ struct rebase_options {
struct commit *onto;
const char *onto_name;
const char *revisions;
+   const char *switch_to;
int root;
struct commit *restrict_revision;
int dont_finish_rebase;
@@ -186,6 +187,8 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->flags & REBASE_DIFFSTAT ? "t" : "");
add_var(_snippet, "force_rebase",
opts->flags & REBASE_FORCE ? "t" : "");
+   if (opts->switch_to)
+   add_var(_snippet, "switch_to", opts->switch_to);
 
switch (opts->type) {
case REBASE_AM:
@@ -564,9 +567,23 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 * orig_head -- commit object name of tip of the branch before rebasing
 * head_name -- refs/heads/ or NULL (detached HEAD)
 */
-   if (argc > 0)
-die("TODO: handle switch_to");
-   else {
+   if (argc == 1) {
+   /* Is it "rebase other branchname" or "rebase other commit"? */
+   branch_name = argv[0];
+   options.switch_to = argv[0];
+
+   /* Is it a local branch? */
+   strbuf_reset();
+   strbuf_addf(, "refs/heads/%s", branch_name);
+   if (!read_ref(buf.buf, _head))
+   options.head_name = xstrdup(buf.buf);
+   /* If not is it a valid ref (branch or commit)? */
+   else if (!get_oid(branch_name, _head))
+   options.head_name = NULL;
+   else
+   die(_("fatal: no such branch/commit '%s'"),
+   branch_name);
+   } else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
@@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
if (get_oid("HEAD", _head))
die(_("Could not resolve HEAD to a revision"));
-   }
+   } else
+   BUG("unexpected number of arguments left to parse");
 
if (read_index(the_repository->index) < 0)
die(_("could not read index"));
@@ -612,6 +630,28 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int flag;
 
if (!(options.flags & REBASE_FORCE)) {
+   /* Lazily switch to the target branch if needed... */
+   if (options.switch_to) {
+   struct object_id oid;
+
+   if (get_oid(options.switch_to, ) < 0) {
+   ret = !!error(_("could not parse '%s'"),
+ options.switch_to);
+   goto cleanup;
+   }
+
+   strbuf_reset();
+   strbuf_addf(, "rebase: checkout %s",
+   options.switch_to);
+   if (reset_head(, "checkout",
+  options.head_name, 0) < 0) {
+   ret = !!error(_("could not switch to "
+   "%s"),
+ options.switch_to);
+   goto cleanup;
+   }
+   }
+
if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
-- 
2.18.0



[PATCH 07/11] builtin rebase: try to fast forward when possible

2018-08-08 Thread Pratik Karki
In this commit, we add support to fast forward.

Note: we will need the merge base later, therefore the call to
can_fast_forward() really needs to be the first one when testing whether
we can skip the rebase entirely (otherwise, it would make more sense to
skip the possibly expensive operation if, say, running an interactive
rebase).

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 72 
 1 file changed, 72 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index afef0b0046..52a218cd18 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -20,6 +20,7 @@
 #include "commit.h"
 #include "diff.h"
 #include "wt-status.h"
+#include "revision.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -89,6 +90,12 @@ struct rebase_options {
struct strbuf git_am_opt;
 };
 
+static int is_interactive(struct rebase_options *opts)
+{
+   return opts->type == REBASE_INTERACTIVE ||
+   opts->type == REBASE_PRESERVE_MERGES;
+}
+
 /* Returns the filename prefixed by the state_dir */
 static const char *state_dir_path(const char *filename, struct rebase_options 
*opts)
 {
@@ -334,6 +341,46 @@ static int rebase_config(const char *var, const char 
*value, void *data)
return git_default_config(var, value, data);
 }
 
+/*
+ * Determines whether the commits in from..to are linear, i.e. contain
+ * no merge commits. This function *expects* `from` to be an ancestor of
+ * `to`.
+ */
+static int is_linear_history(struct commit *from, struct commit *to)
+{
+   while (to && to != from) {
+   parse_commit(to);
+   if (!to->parents)
+   return 1;
+   if (to->parents->next)
+   return 0;
+   to = to->parents->item;
+   }
+   return 1;
+}
+
+static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
+   struct object_id *merge_base)
+{
+   struct commit *head = lookup_commit(the_repository, head_oid);
+   struct commit_list *merge_bases;
+   int res;
+
+   if (!head)
+   return 0;
+
+   merge_bases = get_merge_bases(onto, head);
+   if (merge_bases && !merge_bases->next) {
+   oidcpy(merge_base, _bases->item->object.oid);
+   res = !oidcmp(merge_base, >object.oid);
+   } else {
+   oidcpy(merge_base, _oid);
+   res = 0;
+   }
+   free_commit_list(merge_bases);
+   return res && is_linear_history(onto, head);
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -489,6 +536,31 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
goto cleanup;
}
 
+   /*
+* Now we are rebasing commits upstream..orig_head (or with --root,
+* everything leading up to orig_head) on top of onto.
+*/
+
+   /*
+* Check if we are already based on onto with linear history,
+* but this should be done only when upstream and onto are the same
+* and if this is not an interactive rebase.
+*/
+   if (can_fast_forward(options.onto, _head, _base) &&
+   !is_interactive() && !options.restrict_revision &&
+   !oidcmp(>object.oid, >object.oid)) {
+   int flag;
+
+   if (!(options.flags & REBASE_NO_QUIET))
+   ; /* be quiet */
+   else if (!strcmp(branch_name, "HEAD") &&
+   resolve_ref_unsafe("HEAD", 0, NULL, ))
+   puts(_("HEAD is up to date, rebase forced."));
+   else
+   printf(_("Current branch %s is up to date, rebase "
+"forced.\n"), branch_name);
+   }
+
/* If a hook exists, give it a chance to interrupt*/
if (!ok_to_skip_pre_rebase &&
run_hook_le(NULL, "pre-rebase", options.upstream_arg,
-- 
2.18.0



[PATCH 08/11] builtin rebase: support --force-rebase

2018-08-08 Thread Pratik Karki
In this commit, we add support to `--force-rebase` option. The
equivalent part of the shell script found in `git-legacy-rebase.sh` is
converted as faithfully as possible to C.

The --force-rebase option ensures that the rebase does not simply
fast-forward even if it could.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 24 ++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 52a218cd18..8a7bf3d468 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -86,6 +86,7 @@ struct rebase_options {
REBASE_NO_QUIET = 1<<0,
REBASE_VERBOSE = 1<<1,
REBASE_DIFFSTAT = 1<<2,
+   REBASE_FORCE = 1<<3,
} flags;
struct strbuf git_am_opt;
 };
@@ -181,6 +182,8 @@ static int run_specific_rebase(struct rebase_options *opts)
opts->flags & REBASE_VERBOSE ? "t" : "");
add_var(_snippet, "diffstat",
opts->flags & REBASE_DIFFSTAT ? "t" : "");
+   add_var(_snippet, "force_rebase",
+   opts->flags & REBASE_FORCE ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -409,6 +412,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
{OPTION_NEGBIT, 'n', "no-stat", , NULL,
N_("do not show diffstat of what changed upstream"),
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
+   OPT_BIT('f', "force-rebase", ,
+   N_("cherry-pick all commits, even if unchanged"),
+   REBASE_FORCE),
+   OPT_BIT(0, "no-ff", ,
+   N_("cherry-pick all commits, even if unchanged"),
+   REBASE_FORCE),
OPT_END(),
};
 
@@ -551,10 +560,21 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
!oidcmp(>object.oid, >object.oid)) {
int flag;
 
-   if (!(options.flags & REBASE_NO_QUIET))
+   if (!(options.flags & REBASE_FORCE)) {
+   if (!(options.flags & REBASE_NO_QUIET))
+   ; /* be quiet */
+   else if (!strcmp(branch_name, "HEAD") &&
+resolve_ref_unsafe("HEAD", 0, NULL, ))
+   puts(_("HEAD is up to date."));
+   else
+   printf(_("Current branch %s is up to date.\n"),
+  branch_name);
+   ret = !!finish_rebase();
+   goto cleanup;
+   } else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
else if (!strcmp(branch_name, "HEAD") &&
-   resolve_ref_unsafe("HEAD", 0, NULL, ))
+resolve_ref_unsafe("HEAD", 0, NULL, ))
puts(_("HEAD is up to date, rebase forced."));
else
printf(_("Current branch %s is up to date, rebase "
-- 
2.18.0



[GSoC] [PATCH 00/11] A minimal builtin rebase

2018-08-08 Thread Pratik Karki
This patch series provides the bare minimum to run more than the trivial
rebase (i.e. `git rebase `).

Here, I have implemented essential options needed to make this a
builtin rebase. Ofcourse, to accomplish the task of builtin rebase, I had to
do essential optimizations and add certain shield which weren't present in
original rebase.

It is based the latest iteration of pk/rebase-in-c, i.e. ac7f467fef8b
(builtin/rebase: support running "git rebase ", 2018-08-07).

This is the second patch series that brings us more closer to a
builtin "git rebase".
If you like to view the development branch, you can view
(https://github.com/git/git/pull/505), where I have kept my commits up to date
and leveraged Travis(there is sporadic failures in t5520 for macos gcc and
isn't due to my patches) for extra testing other than my system.

I plan on submitting the next patch series today, in this order:

bultin rebase actions: The builtin rebase will add all the rebase actions.
builtin rebase options: The builtin rebase will add all the options supported
by original rebase.
builtin rebase rest: The builtin rebase will convert all the remaining shell
scripts from the original rebase to C.
default to builtin rebase: This will turn on the feature-complete builtin
rebase to on.

These patch series are built on top of each other, i.e. they depend on this 
order.

The motivation to organize these patch series is to make review easier and
for pleasant read with the help of my GSoC mentors since, this is the final
week of GSoC and other GSoC students most likely have submitted their
respective works which will need lots of review.

Pratik Karki (11):
  builtin rebase: support --onto
  builtin rebase: support `git rebase --onto A...B`
  builtin rebase: handle the pre-rebase hook (and add --no-verify)
  builtin rebase: support --quiet
  builtin rebase: support the `verbose` and `diffstat` options
  builtin rebase: require a clean worktree
  builtin rebase: try to fast forward when possible
  builtin rebase: support --force-rebase
  builtin rebase: start a new rebase only if none is in progress
  builtin rebase: only store fully-qualified refs in `options.head_name`
  builtin rebase: support `git rebase  `

 builtin/rebase.c | 333 +--
 1 file changed, 320 insertions(+), 13 deletions(-)

-- 
2.18.0



[PATCH 05/11] builtin rebase: support the `verbose` and `diffstat` options

2018-08-08 Thread Pratik Karki
This commit introduces support for the `-v` and `--stat` options of
rebase.

The --stat option can also be configured via the Git config setting
rebase.stat. To support this, we also add a custom rebase_config()
function in this commit that will be used instead of (and falls back to
calling) git_default_config().

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 60 ++--
 1 file changed, 58 insertions(+), 2 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 19fa4d3fc4..2d3f1d65fb 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -18,6 +18,7 @@
 #include "lockfile.h"
 #include "parse-options.h"
 #include "commit.h"
+#include "diff.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -81,6 +82,8 @@ struct rebase_options {
int dont_finish_rebase;
enum {
REBASE_NO_QUIET = 1<<0,
+   REBASE_VERBOSE = 1<<1,
+   REBASE_DIFFSTAT = 1<<2,
} flags;
struct strbuf git_am_opt;
 };
@@ -166,6 +169,10 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "GIT_QUIET",
opts->flags & REBASE_NO_QUIET ? "" : "t");
add_var(_snippet, "git_am_opt", opts->git_am_opt.buf);
+   add_var(_snippet, "verbose",
+   opts->flags & REBASE_VERBOSE ? "t" : "");
+   add_var(_snippet, "diffstat",
+   opts->flags & REBASE_DIFFSTAT ? "t" : "");
 
switch (opts->type) {
case REBASE_AM:
@@ -311,6 +318,21 @@ static int reset_head(struct object_id *oid, const char 
*action,
return ret;
 }
 
+static int rebase_config(const char *var, const char *value, void *data)
+{
+   struct rebase_options *opts = data;
+
+   if (!strcmp(var, "rebase.stat")) {
+   if (git_config_bool(var, value))
+   opts->flags |= REBASE_DIFFSTAT;
+   else
+   opts->flags &= !REBASE_DIFFSTAT;
+   return 0;
+   }
+
+   return git_default_config(var, value, data);
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
struct rebase_options options = {
@@ -332,7 +354,13 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", ,
   N_("be quiet. implies --no-stat"),
-  REBASE_NO_QUIET),
+  REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+   OPT_BIT('v', "verbose", ,
+   N_("display a diffstat of what changed upstream"),
+   REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
+   {OPTION_NEGBIT, 'n', "no-stat", , NULL,
+   N_("do not show diffstat of what changed upstream"),
+   PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
OPT_END(),
};
 
@@ -360,7 +388,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
trace_repo_setup(prefix);
setup_work_tree();
 
-   git_config(git_default_config, NULL);
+   git_config(rebase_config, );
+
argc = parse_options(argc, argv, prefix,
 builtin_rebase_options,
 builtin_rebase_usage, 0);
@@ -456,6 +485,33 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
argc ? argv[0] : NULL, NULL))
die(_("The pre-rebase hook refused to rebase."));
 
+   if (options.flags & REBASE_DIFFSTAT) {
+   struct diff_options opts;
+
+   if (options.flags & REBASE_VERBOSE)
+   printf(_("Changes from %s to %s:\n"),
+   oid_to_hex(_base),
+   oid_to_hex(>object.oid));
+
+   /* We want color (if set), but no pager */
+   diff_setup();
+   opts.stat_width = -1; /* use full terminal width */
+   opts.stat_graph_width = -1; /* respect statGraphWidth config */
+   opts.output_format |=
+   DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+   opts.detect_rename = DIFF_DETECT_RENAME;
+   diff_setup_done();
+   diff_tree_oid(_base, >object.oid,
+ "", );
+   diffcore_std();
+   diff_flush();
+   }
+
+   /* Detach HEAD and reset the tree */
+   if (options.flags & REBASE_NO_QUIET)
+   printf(_("First, rewinding head to replay your work on top of "
+"it...\n"));
+
strbuf_addf(, "rebase: checkout %s", options.onto_name);
if (reset_head(>object.oid, "checkout", NULL, 1))
die(_("Could not detach HEAD"));
-- 
2.18.0



[PATCH 02/11] builtin rebase: support `git rebase --onto A...B`

2018-08-08 Thread Pratik Karki
This commit implements support for an --onto argument that is actually a
"symmetric range" i.e. `...`.

The equivalent shell script version of the code offers two different
error messages for the cases where there is no merge base vs more than
one merge base. Though following the similar approach would be nice,
this would create more complexity than it is of current. Currently, for
simple convenience, the `get_oid_mb()` function is used whose return
value does not discern between those two error conditions.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 8 +++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 742ed31498..38c496dd10 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -17,6 +17,7 @@
 #include "unpack-trees.h"
 #include "lockfile.h"
 #include "parse-options.h"
+#include "commit.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -311,6 +312,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int ret, flags;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
+   struct object_id merge_base;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", _name,
   N_("revision"),
@@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
-   die("TODO");
+   if (get_oid_mb(options.onto_name, _base) < 0)
+   die(_("'%s': need exactly one merge base"),
+   options.onto_name);
+   options.onto = lookup_commit_or_die(_base,
+   options.onto_name);
} else {
options.onto = peel_committish(options.onto_name);
if (!options.onto)
-- 
2.18.0



[PATCH 01/11] builtin rebase: support --onto

2018-08-08 Thread Pratik Karki
The `--onto` option is important, as it allows to rebase a range of
commits onto a different base commit (which gave the command its odd
name: "rebase").

This commit introduces options parsing so that different options can
be added in future commits.

Note: As this commit introduces to the parse_options() call (which
"eats" argv[0]), the argc is now expected to be lower by one after this
patch, compared to before this patch: argv[0] no longer refers to the
command name, but to the first (non-option) command-line parameter.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 35 ++-
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index e695d8a430..742ed31498 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -16,6 +16,16 @@
 #include "cache-tree.h"
 #include "unpack-trees.h"
 #include "lockfile.h"
+#include "parse-options.h"
+
+static char const * const builtin_rebase_usage[] = {
+   N_("git rebase [-i] [options] [--exec ] [--onto ] "
+   "[] []"),
+   N_("git rebase [-i] [options] [--exec ] [--onto ] "
+   "--root []"),
+   N_("git rebase --continue | --abort | --skip | --edit-todo"),
+   NULL
+};
 
 static GIT_PATH_FUNC(apply_dir, "rebase-apply")
 static GIT_PATH_FUNC(merge_dir, "rebase-merge")
@@ -301,6 +311,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
int ret, flags;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
+   struct option builtin_rebase_options[] = {
+   OPT_STRING(0, "onto", _name,
+  N_("revision"),
+  N_("rebase onto given branch instead of upstream")),
+   OPT_END(),
+   };
 
/*
 * NEEDSWORK: Once the builtin rebase has been tested enough
@@ -318,13 +334,22 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
BUG("sane_execvp() returned???");
}
 
-   if (argc != 2)
-   die(_("Usage: %s "), argv[0]);
+   if (argc == 2 && !strcmp(argv[1], "-h"))
+   usage_with_options(builtin_rebase_usage,
+  builtin_rebase_options);
+
prefix = setup_git_directory();
trace_repo_setup(prefix);
setup_work_tree();
 
git_config(git_default_config, NULL);
+   argc = parse_options(argc, argv, prefix,
+builtin_rebase_options,
+builtin_rebase_usage, 0);
+
+   if (argc > 2)
+   usage_with_options(builtin_rebase_usage,
+  builtin_rebase_options);
 
switch (options.type) {
case REBASE_MERGE:
@@ -343,10 +368,10 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
}
 
if (!options.root) {
-   if (argc < 2)
+   if (argc < 1)
die("TODO: handle @{upstream}");
else {
-   options.upstream_name = argv[1];
+   options.upstream_name = argv[0];
argc--;
argv++;
if (!strcmp(options.upstream_name, "-"))
@@ -377,7 +402,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 * orig_head -- commit object name of tip of the branch before rebasing
 * head_name -- refs/heads/ or "detached HEAD"
 */
-   if (argc > 1)
+   if (argc > 0)
 die("TODO: handle switch_to");
else {
/* Do not need to switch branches, we are already on it. */
-- 
2.18.0



[PATCH 06/11] builtin rebase: require a clean worktree

2018-08-08 Thread Pratik Karki
This commit reads the index of the repository for rebase and checks
whether the repository is ready for rebase.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 2d3f1d65fb..afef0b0046 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -19,6 +19,7 @@
 #include "parse-options.h"
 #include "commit.h"
 #include "diff.h"
+#include "wt-status.h"
 
 static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec ] [--onto ] "
@@ -479,6 +480,15 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("Could not resolve HEAD to a revision"));
}
 
+   if (read_index(the_repository->index) < 0)
+   die(_("could not read index"));
+
+   if (require_clean_work_tree("rebase",
+   _("Please commit or stash them."), 1, 1)) {
+   ret = 1;
+   goto cleanup;
+   }
+
/* If a hook exists, give it a chance to interrupt*/
if (!ok_to_skip_pre_rebase &&
run_hook_le(NULL, "pre-rebase", options.upstream_arg,
@@ -528,6 +538,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 
ret = !!run_specific_rebase();
 
+cleanup:
strbuf_release();
free(options.head_name);
return ret;
-- 
2.18.0



[PATCH 03/11] builtin rebase: handle the pre-rebase hook (and add --no-verify)

2018-08-08 Thread Pratik Karki
This commit converts the equivalent part of the shell script
`git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and
to interrupt the rebase with error if the hook fails.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 11 +++
 1 file changed, 11 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 38c496dd10..b79f9b0a9f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -70,6 +70,7 @@ struct rebase_options {
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
+   const char *upstream_arg;
char *head_name;
struct object_id orig_head;
struct commit *onto;
@@ -310,6 +311,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
};
const char *branch_name;
int ret, flags;
+   int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
struct object_id merge_base;
@@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
OPT_STRING(0, "onto", _name,
   N_("revision"),
   N_("rebase onto given branch instead of upstream")),
+   OPT_BOOL(0, "no-verify", _to_skip_pre_rebase,
+N_("allow pre-rebase hook to run")),
OPT_END(),
};
 
@@ -382,6 +386,7 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
options.upstream = peel_committish(options.upstream_name);
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
+   options.upstream_arg = options.upstream_name;
} else
die("TODO: upstream for --root");
 
@@ -430,6 +435,12 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
die(_("Could not resolve HEAD to a revision"));
}
 
+   /* If a hook exists, give it a chance to interrupt*/
+   if (!ok_to_skip_pre_rebase &&
+   run_hook_le(NULL, "pre-rebase", options.upstream_arg,
+   argc ? argv[0] : NULL, NULL))
+   die(_("The pre-rebase hook refused to rebase."));
+
strbuf_addf(, "rebase: checkout %s", options.onto_name);
if (reset_head(>object.oid, "checkout", NULL, 1))
die(_("Could not detach HEAD"));
-- 
2.18.0



[PATCH 04/11] builtin rebase: support --quiet

2018-08-08 Thread Pratik Karki
This commit introduces a rebase option `--quiet`. While `--quiet` is
commonly perceived as opposite to `--verbose`, this is not the case for
the rebase command: both `--quiet` and `--verbose` default to `false` if
neither `--quiet` nor `--verbose` is present.

This commit goes further and introduces `--no-quiet` which is the
contrary of `--quiet` and it's introduction doesn't modify any
behaviour.

Note: The `flags` field in `rebase_options` will accumulate more bits in
subsequent commits, in particular a verbose and a diffstat flag. And as
--quoet inthe shell scripted version of the rebase command switches off
--verbose and --stat, and as --verbose switches off --quiet, we use the
(negated) REBASE_NO_QUIET instead of REBASE_QUIET: this allows us to
turn off the quiet mode and turn on the verbose and diffstat mode in a
single OPT_BIT(), and the opposite in a single OPT_NEGBIT().

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 15 +++
 1 file changed, 15 insertions(+)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index b79f9b0a9f..19fa4d3fc4 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -79,6 +79,10 @@ struct rebase_options {
int root;
struct commit *restrict_revision;
int dont_finish_rebase;
+   enum {
+   REBASE_NO_QUIET = 1<<0,
+   } flags;
+   struct strbuf git_am_opt;
 };
 
 /* Returns the filename prefixed by the state_dir */
@@ -159,6 +163,9 @@ static int run_specific_rebase(struct rebase_options *opts)
add_var(_snippet, "revisions", opts->revisions);
add_var(_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(>restrict_revision->object.oid) : NULL);
+   add_var(_snippet, "GIT_QUIET",
+   opts->flags & REBASE_NO_QUIET ? "" : "t");
+   add_var(_snippet, "git_am_opt", opts->git_am_opt.buf);
 
switch (opts->type) {
case REBASE_AM:
@@ -308,6 +315,8 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
 {
struct rebase_options options = {
.type = REBASE_UNSPECIFIED,
+   .flags = REBASE_NO_QUIET,
+   .git_am_opt = STRBUF_INIT,
};
const char *branch_name;
int ret, flags;
@@ -321,6 +330,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
   N_("rebase onto given branch instead of upstream")),
OPT_BOOL(0, "no-verify", _to_skip_pre_rebase,
 N_("allow pre-rebase hook to run")),
+   OPT_NEGBIT('q', "quiet", ,
+  N_("be quiet. implies --no-stat"),
+  REBASE_NO_QUIET),
OPT_END(),
};
 
@@ -357,6 +369,9 @@ int cmd_rebase(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_rebase_usage,
   builtin_rebase_options);
 
+   if (!(options.flags & REBASE_NO_QUIET))
+   strbuf_addstr(_am_opt, " -q");
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
-- 
2.18.0



[GSoC] [PATCH v6 2/3] rebase: refactor common shell functions into their own file

2018-08-06 Thread Pratik Karki
The functions present in `git-legacy-rebase.sh` are used by the rebase
backends as they are implemented as shell script functions in the
`git-rebase--` files.

To make the `builtin/rebase.c` work, we have to provide support via
a Unix shell script snippet that uses these functions and so, we
want to use the rebase backends *directly* from the builtin rebase
without going through `git-legacy-rebase.sh`.

This commit extracts the functions to a separate file,
`git-rebase--common`, that will be read by `git-legacy-rebase.sh` and
by the shell script snippets which will be used extensively in the
following commits.

Signed-off-by: Pratik Karki 
---
Unchanged since v5.

 .gitignore|  1 +
 Makefile  |  1 +
 git-legacy-rebase.sh  | 69 ++-
 git-rebase--common.sh | 68 ++
 4 files changed, 72 insertions(+), 67 deletions(-)
 create mode 100644 git-rebase--common.sh

diff --git a/.gitignore b/.gitignore
index ec23959014..824141cba1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@
 /git-read-tree
 /git-rebase
 /git-rebase--am
+/git-rebase--common
 /git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
diff --git a/Makefile b/Makefile
index c311532791..c210681a1d 100644
--- a/Makefile
+++ b/Makefile
@@ -619,6 +619,7 @@ 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--common
 SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
diff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh
index 7973447645..af2cdfef03 100755
--- a/git-legacy-rebase.sh
+++ b/git-legacy-rebase.sh
@@ -57,12 +57,7 @@ cd_to_toplevel
 LF='
 '
 ok_to_skip_pre_rebase=
-resolvemsg="
-$(gettext 'Resolve all conflicts manually, mark them as resolved with
-"git add/rm ", then run "git rebase --continue".
-You can instead skip this commit: run "git rebase --skip".
-To abort and get back to the state before "git rebase", run "git rebase 
--abort".')
-"
+
 squash_onto=
 unset onto
 unset restrict_revision
@@ -102,6 +97,7 @@ case "$(git config --bool commit.gpgsign)" in
 true)  gpg_sign_opt=-S ;;
 *) gpg_sign_opt= ;;
 esac
+. git-rebase--common
 
 read_basic_state () {
test -f "$state_dir/head-name" &&
@@ -132,67 +128,6 @@ read_basic_state () {
}
 }
 
-write_basic_state () {
-   echo "$head_name" > "$state_dir"/head-name &&
-   echo "$onto" > "$state_dir"/onto &&
-   echo "$orig_head" > "$state_dir"/orig-head &&
-   echo "$GIT_QUIET" > "$state_dir"/quiet &&
-   test t = "$verbose" && : > "$state_dir"/verbose
-   test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
-   test -n "$strategy_opts" && echo "$strategy_opts" > \
-   "$state_dir"/strategy_opts
-   test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > 
\
-   "$state_dir"/allow_rerere_autoupdate
-   test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > 
"$state_dir"/gpg_sign_opt
-   test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
-}
-
-output () {
-   case "$verbose" in
-   '')
-   output=$("$@" 2>&1 )
-   status=$?
-   test $status != 0 && printf "%s\n" "$output"
-   return $status
-   ;;
-   *)
-   "$@"
-   ;;
-   esac
-}
-
-move_to_original_branch () {
-   case "$head_name" in
-   refs/*)
-   message="rebase finished: $head_name onto $onto"
-   git update-ref -m "$message" \
-   $head_name $(git rev-parse HEAD) $orig_head &&
-   git symbolic-ref \
-   -m "rebase finished: returning to $head_name" \
-   HEAD $head_name ||
-   die "$(eval_gettext "Could not move back to \$head_name")"
-   ;;
-   esac
-}
-
-apply_autostash () {
-   if test -f "$state_dir/autostash"
-   then
-   stash_sha1=$(cat "$state_dir/autostash")
-   if git stash apply $stash_sha1 >/dev/null 2>&1
-   then
-   echo "$(gettext 'Applied autostash.')" >&2
-   else
-   git stash store -m &q

[GSoC] [PATCH v6 1/3] rebase: start implementing it as a builtin

2018-08-06 Thread Pratik Karki
This commit imitates the strategy that was used to convert the
difftool to a builtin. We start by renaming the shell script
`git-rebase.sh` to `git-legacy-rebase.sh` and introduce a
`builtin/rebase.c` that simply executes the shell script version,
unless the config setting `rebase.useBuiltin` is set to `true`.

The motivation behind this is to rewrite all the functionality of the
shell script version in the aforementioned `rebase.c`, one by one and
be able to conveniently test new features by configuring
`rebase.useBuiltin`.

In the original difftool conversion, if sane_execvp() that attempts to
run the legacy scripted version returned with non-negative status, the
command silently exited without doing anything with success, but
sane_execvp() should not return with non-negative status in the first
place, so we use die() to notice such an abnormal case.

We intentionally avoid reading the config directly to avoid
messing up the GIT_* environment variables when we need to fall back to
exec()ing the shell script. The test of builtin rebase can be done by
`git -c rebase.useBuiltin=true rebase ...`

Signed-off-by: Pratik Karki 
---
Unchanged since v5.

 .gitignore|  1 +
 Makefile  |  3 +-
 builtin.h |  1 +
 builtin/rebase.c  | 58 +++
 git-rebase.sh => git-legacy-rebase.sh |  0
 git.c |  6 +++
 6 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (100%)

diff --git a/.gitignore b/.gitignore
index 3284a1e9b1..ec23959014 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-legacy-rebase
 /git-log
 /git-ls-files
 /git-ls-remote
diff --git a/Makefile b/Makefile
index bc4fc8eeab..c311532791 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,7 @@ 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-legacy-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -1063,6 +1063,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/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index 0362f1ce25..44651a447f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -202,6 +202,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_rebase__helper(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);
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 00..c44addb2a4
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,58 @@
+/*
+ * "git rebase" builtin command
+ *
+ * Copyright (c) 2018 Pratik Karki
+ */
+
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "argv-array.h"
+#include "dir.h"
+
+static int use_builtin_rebase(void)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+   struct strbuf out = STRBUF_INIT;
+   int ret;
+
+   argv_array_pushl(,
+"config", "--bool", "rebase.usebuiltin", NULL);
+   cp.git_cmd = 1;
+   if (capture_command(, , 6)) {
+   strbuf_release();
+   return 0;
+   }
+
+   strbuf_trim();
+   ret = !strcmp("true", out.buf);
+   strbuf_release();
+   return ret;
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+   /*
+* NEEDSWORK: Once the builtin rebase has been tested enough
+* and git-legacy-rebase.sh is retired to contrib/, this preamble
+* can be removed.
+*/
+
+   if (!use_builtin_rebase()) {
+   const char *path = mkpath("%s/git-legacy-rebase",
+ git_exec_path());
+
+   if (sane_execvp(path, (char **)argv) < 0)
+   die_errno(_("could not exec %s"), path);
+   else
+   BUG("sane_execvp() returned???");
+   }
+
+   if (argc != 2)
+   die(_("Usage: %s ")

[GSoC] [PATCH v6 0/3] rebase: rewrite rebase in C

2018-08-06 Thread Pratik Karki
As a GSoC project, I have been working on the builtin rebase.

The motivation behind the rewrite of rebase i.e. from shell script to C
are for following reasons:

1.  Writing shell scripts and getting it to production is much faster
than doing the equivalent in C but lacks in performance and extra
workarounds are needed for non-POSIX platforms.

2.  Git for Windows is at loss as the installer size increases due to
addition of extra dependencies for the shell scripts which are usually
available in POSIX compliant platforms.

This series of patches serves to demonstrate a minimal builtin rebase
which supports running `git rebase ` and also serves to ask for
reviews.

This is only the first patch series, with  many more to come.
I have finished the conversion, but I want to organize the patches in a neat
patch series to make review process more pleasant, and you can see the progress
at <https://github.com/git/git/pull/505>.

The organization of patches is also almost done. After the follow-up patches,
the rebase operation will be handled completely by this `builtin/rebase.c`.

Changes since v5:

  -  Fix `builtin/rebase: support running "git rebase "` as it
 does not need to switch to another branch and only needs to detach the
 `HEAD` for now.

  -  The `reset_head()` function is introduced in
 `builtin/rebase: support running "git rebase "`
 which is only used to detach the `HEAD` to `onto` for starting the rebase,
 but upcoming patches will add more functionality like moving or updating to
 original branch.

  -  Lots of changes that fix bugs discovered while getting the test suite to 
pass
     with the patch series.

Pratik Karki (3):
  rebase: start implementing it as a builtin
  rebase: refactor common shell functions into their own file
  builtin/rebase: support running "git rebase "

 .gitignore|   2 +
 Makefile  |   4 +-
 builtin.h |   1 +
 builtin/rebase.c  | 421 ++
 git-rebase.sh => git-legacy-rebase.sh |  69 +
 git-rebase--common.sh |  68 +
 git.c |   6 +
 7 files changed, 503 insertions(+), 68 deletions(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (90%)
 create mode 100644 git-rebase--common.sh

-- 
2.18.0



[GSoC] [PATCH v6 3/3] builtin/rebase: support running "git rebase "

2018-08-06 Thread Pratik Karki
This patch gives life to the skeleton added in the previous patches:
With this change, we can perform a elementary rebase (without any
options).

It can be tested thusly by:

git -c rebase.usebuiltin=true rebase HEAD~2

The rebase backends (i.e. the shell script functions defined in
`git-rebase--`) are still at work here and the "builtin
rebase"'s purpose is simply to parse the options and set
everything up so that those rebase backends can do their work.

Note: We take an alternative approach here which is *not* to set the
environment variables via `run_command_v_opt_cd_env()` because those
variables would then be visible by processes spawned from the rebase
backends. Instead, we work hard to set them in the shell script snippet.
On Windows, some of the tests fail merely due to core.fileMode not
being heeded that's why the core.*config variables is parsed here.

The `reset_head()` function is currently only used to detach the HEAD
to onto by way of starting the rebase, but it offers additional
functionality that subsequent patches will need like moving to the
original branch (possibly updating it) and also to do the equivalent of
`git reset --hard`.

The next commits will handle and support all the wonderful rebase
options.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 365 ++-
 1 file changed, 364 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index c44addb2a4..e695d8a430 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -9,6 +9,24 @@
 #include "exec-cmd.h"
 #include "argv-array.h"
 #include "dir.h"
+#include "packfile.h"
+#include "refs.h"
+#include "quote.h"
+#include "config.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "lockfile.h"
+
+static GIT_PATH_FUNC(apply_dir, "rebase-apply")
+static GIT_PATH_FUNC(merge_dir, "rebase-merge")
+
+enum rebase_type {
+   REBASE_UNSPECIFIED = -1,
+   REBASE_AM,
+   REBASE_MERGE,
+   REBASE_INTERACTIVE,
+   REBASE_PRESERVE_MERGES
+};
 
 static int use_builtin_rebase(void)
 {
@@ -30,8 +48,260 @@ static int use_builtin_rebase(void)
return ret;
 }
 
+static int apply_autostash(void)
+{
+   warning("TODO");
+   return 0;
+}
+
+struct rebase_options {
+   enum rebase_type type;
+   const char *state_dir;
+   struct commit *upstream;
+   const char *upstream_name;
+   char *head_name;
+   struct object_id orig_head;
+   struct commit *onto;
+   const char *onto_name;
+   const char *revisions;
+   int root;
+   struct commit *restrict_revision;
+   int dont_finish_rebase;
+};
+
+/* Returns the filename prefixed by the state_dir */
+static const char *state_dir_path(const char *filename, struct rebase_options 
*opts)
+{
+   static struct strbuf path = STRBUF_INIT;
+   static size_t prefix_len;
+
+   if (!prefix_len) {
+   strbuf_addf(, "%s/", opts->state_dir);
+   prefix_len = path.len;
+   }
+
+   strbuf_setlen(, prefix_len);
+   strbuf_addstr(, filename);
+   return path.buf;
+}
+
+static int finish_rebase(struct rebase_options *opts)
+{
+   struct strbuf dir = STRBUF_INIT;
+   const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+
+   delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+   apply_autostash();
+   close_all_packs(the_repository->objects);
+   /*
+* We ignore errors in 'gc --auto', since the
+* user should see them.
+*/
+   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+   strbuf_addstr(, opts->state_dir);
+   remove_dir_recursively(, 0);
+   strbuf_release();
+
+   return 0;
+}
+
+static struct commit *peel_committish(const char *name)
+{
+   struct object *obj;
+   struct object_id oid;
+
+   if (get_oid(name, ))
+   return NULL;
+   obj = parse_object(the_repository, );
+   return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
+static void add_var(struct strbuf *buf, const char *name, const char *value)
+{
+   if (!value)
+   strbuf_addf(buf, "unset %s; ", name);
+   else {
+   strbuf_addf(buf, "%s=", name);
+   sq_quote_buf(buf, value);
+   strbuf_addstr(buf, "; ");
+   }
+}
+
+static int run_specific_rebase(struct rebase_options *opts)
+{
+   const char *argv[] = { NULL, NULL };
+   struct strbuf script_snippet = STRBUF_INIT;
+   int status;
+   const char *backend, *backend_func;
+
+   add_var(_snippet, "GIT_DIR", absolute_path(get_git_dir()));
+   add_var(_snippet, "state_dir", opts->state_dir);
+
+   add_var(_snippet, "upstream_name", opts->upstream_name

Re: What's cooking in git.git (Aug 2018, #01; Thu, 2)

2018-08-02 Thread Pratik Karki
Hi Junio,

On Fri, Aug 3, 2018 at 4:47 AM Junio C Hamano  wrote:
>
> * pk/rebase-in-c (2018-07-30) 3 commits
>  - builtin/rebase: support running "git rebase "
>  - rebase: refactor common shell functions into their own file
>  - rebase: start implementing it as a builtin
>
>  Rewrite of the "rebase" machinery in C.
>
>  Will merge to 'next'.

On a recent mail[1], I had suggested stalling this iteration in `pu`
and wait for another iteration to merge in `next`.

[1]: 
https://public-inbox.org/git/CAOZc8M_FJmMCEB1MkJrBRpLiFjy8OTEg_MxoNQTh5-brHxR-=a...@mail.gmail.com/

Cheers,
Pratik


Re: [GSoC] [PATCH v5 0/3] rebase: rewrite rebase in C

2018-07-31 Thread Pratik Karki
Hi Junio,

During recent development, I found out that `v5` has some issues and shouldn't
be merged into `next`. I implemented more options and ran a couple of regression
tests from which I figured out that certain choices I made in those commits
need to be reconsidered.

During recent development, my working branch `wip-rebase` has passing `t3400`
and for which I have made some changes to the code already in v5.

Cheers,
Pratik


[PATCH v5 3/3] builtin/rebase: support running "git rebase "

2018-07-30 Thread Pratik Karki
This patch gives life to the skeleton added in the previous patches:
With this change, we can perform a elementary rebase (without any
options).

It can be tested thusly by:

git -c rebase.usebuiltin=true rebase HEAD~2

The rebase backends (i.e. the shell script functions defined in
`git-rebase--`) are still at work here and the "builtin
rebase"'s purpose is simply to parse the options and set
everything up so that those rebase backends can do their work.

Note: We take an alternative approach here which is *not* to set the
environment variables via `run_command_v_opt_cd_env()` because those
variables would then be visible by processes spawned from the rebase
backends. Instead, we work hard to set them in the shell script snippet.
On Windows, some of the tests fail merely due to core.fileMode not
being heeded that's why the core.*config variables is parsed here.

The next commits will handle and support all the wonderful rebase
options.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 350 ++-
 1 file changed, 349 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index c44addb2a4..5863139204 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -9,6 +9,24 @@
 #include "exec-cmd.h"
 #include "argv-array.h"
 #include "dir.h"
+#include "packfile.h"
+#include "refs.h"
+#include "quote.h"
+#include "config.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "lockfile.h"
+
+static GIT_PATH_FUNC(apply_dir, "rebase-apply")
+static GIT_PATH_FUNC(merge_dir, "rebase-merge")
+
+enum rebase_type {
+   REBASE_UNSPECIFIED = -1,
+   REBASE_AM,
+   REBASE_MERGE,
+   REBASE_INTERACTIVE,
+   REBASE_PRESERVE_MERGES
+};
 
 static int use_builtin_rebase(void)
 {
@@ -30,8 +48,243 @@ static int use_builtin_rebase(void)
return ret;
 }
 
+static int apply_autostash(void)
+{
+   warning("TODO");
+   return 0;
+}
+
+struct rebase_options {
+   enum rebase_type type;
+   const char *state_dir;
+   struct commit *upstream;
+   const char *upstream_name;
+   char *head_name;
+   struct object_id orig_head;
+   struct commit *onto;
+   const char *onto_name;
+   const char *revisions;
+   const char *root;
+   const char *restrict_revision;
+};
+
+static int finish_rebase(struct rebase_options *opts)
+{
+   struct strbuf dir = STRBUF_INIT;
+   const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+
+   delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+   apply_autostash();
+   close_all_packs(the_repository->objects);
+   /*
+* We ignore errors in 'gc --auto', since the
+* user should see them.
+*/
+   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+   strbuf_addstr(, opts->state_dir);
+   remove_dir_recursively(, 0);
+   strbuf_release();
+
+   return 0;
+}
+
+static struct commit *peel_committish(const char *name)
+{
+   struct object *obj;
+   struct object_id oid;
+
+   if (get_oid(name, ))
+   return NULL;
+   obj = parse_object();
+   return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
+static void add_var(struct strbuf *buf, const char *name, const char *value)
+{
+   strbuf_addstr(buf, name);
+   if (value) {
+   strbuf_addstr(buf, "=");
+   sq_quote_buf(buf, value);
+   }
+   strbuf_addstr(buf, "; ");
+}
+
+static int run_specific_rebase(struct rebase_options *opts)
+{
+   const char *argv[] = { NULL, NULL };
+   struct strbuf script_snippet = STRBUF_INIT;
+   int status;
+   const char *backend, *backend_func;
+
+   add_var(_snippet, "GIT_DIR", absolute_path(get_git_dir()));
+   add_var(_snippet, "state_dir", opts->state_dir);
+
+   add_var(_snippet, "upstream_name", opts->upstream_name);
+   add_var(_snippet, "upstream",
+oid_to_hex(>upstream->object.oid));
+   add_var(_snippet, "head_name", opts->head_name);
+   add_var(_snippet, "orig_head", oid_to_hex(>orig_head));
+   add_var(_snippet, "onto", oid_to_hex(>onto->object.oid));
+   add_var(_snippet, "onto_name", opts->onto_name);
+   add_var(_snippet, "revisions", opts->revisions);
+   if (opts->restrict_revision == NULL)
+   add_var(_snippet, "restrict_revision", "");
+   else
+   add_var(_snippet, "restrict_revision",
+   opts->restrict_revision);
+
+   switch (opts->type) {
+   case REBASE_AM:
+   backend = "git-rebase--am"

[PATCH v5 2/3] rebase: refactor common shell functions into their own file

2018-07-30 Thread Pratik Karki
The functions present in `git-legacy-rebase.sh` are used by the rebase
backends as they are implemented as shell script functions in the
`git-rebase--` files.

To make the `builtin/rebase.c` work, we have to provide support via
a Unix shell script snippet that uses these functions and so, we
want to use the rebase backends *directly* from the builtin rebase
without going through `git-legacy-rebase.sh`.

This commit extracts the functions to a separate file,
`git-rebase--common`, that will be read by `git-legacy-rebase.sh` and
by the shell script snippets which will be used extensively in the
following commits.

Signed-off-by: Pratik Karki 
---
Unchanged since v4.

 .gitignore|  1 +
 Makefile  |  1 +
 git-legacy-rebase.sh  | 69 ++-
 git-rebase--common.sh | 68 ++
 4 files changed, 72 insertions(+), 67 deletions(-)
 create mode 100644 git-rebase--common.sh

diff --git a/.gitignore b/.gitignore
index ec23959014..824141cba1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@
 /git-read-tree
 /git-rebase
 /git-rebase--am
+/git-rebase--common
 /git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
diff --git a/Makefile b/Makefile
index a0c410b7d6..c59e2f64a1 100644
--- a/Makefile
+++ b/Makefile
@@ -619,6 +619,7 @@ 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--common
 SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
diff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh
index 7973447645..af2cdfef03 100755
--- a/git-legacy-rebase.sh
+++ b/git-legacy-rebase.sh
@@ -57,12 +57,7 @@ cd_to_toplevel
 LF='
 '
 ok_to_skip_pre_rebase=
-resolvemsg="
-$(gettext 'Resolve all conflicts manually, mark them as resolved with
-"git add/rm ", then run "git rebase --continue".
-You can instead skip this commit: run "git rebase --skip".
-To abort and get back to the state before "git rebase", run "git rebase 
--abort".')
-"
+
 squash_onto=
 unset onto
 unset restrict_revision
@@ -102,6 +97,7 @@ case "$(git config --bool commit.gpgsign)" in
 true)  gpg_sign_opt=-S ;;
 *) gpg_sign_opt= ;;
 esac
+. git-rebase--common
 
 read_basic_state () {
test -f "$state_dir/head-name" &&
@@ -132,67 +128,6 @@ read_basic_state () {
}
 }
 
-write_basic_state () {
-   echo "$head_name" > "$state_dir"/head-name &&
-   echo "$onto" > "$state_dir"/onto &&
-   echo "$orig_head" > "$state_dir"/orig-head &&
-   echo "$GIT_QUIET" > "$state_dir"/quiet &&
-   test t = "$verbose" && : > "$state_dir"/verbose
-   test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
-   test -n "$strategy_opts" && echo "$strategy_opts" > \
-   "$state_dir"/strategy_opts
-   test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > 
\
-   "$state_dir"/allow_rerere_autoupdate
-   test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > 
"$state_dir"/gpg_sign_opt
-   test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
-}
-
-output () {
-   case "$verbose" in
-   '')
-   output=$("$@" 2>&1 )
-   status=$?
-   test $status != 0 && printf "%s\n" "$output"
-   return $status
-   ;;
-   *)
-   "$@"
-   ;;
-   esac
-}
-
-move_to_original_branch () {
-   case "$head_name" in
-   refs/*)
-   message="rebase finished: $head_name onto $onto"
-   git update-ref -m "$message" \
-   $head_name $(git rev-parse HEAD) $orig_head &&
-   git symbolic-ref \
-   -m "rebase finished: returning to $head_name" \
-   HEAD $head_name ||
-   die "$(eval_gettext "Could not move back to \$head_name")"
-   ;;
-   esac
-}
-
-apply_autostash () {
-   if test -f "$state_dir/autostash"
-   then
-   stash_sha1=$(cat "$state_dir/autostash")
-   if git stash apply $stash_sha1 >/dev/null 2>&1
-   then
-   echo "$(gettext 'Applied autostash.')" >&2
-   else
-   git stash store -m &q

[PATCH v5 1/3] rebase: start implementing it as a builtin

2018-07-30 Thread Pratik Karki
This commit imitates the strategy that was used to convert the
difftool to a builtin. We start by renaming the shell script
`git-rebase.sh` to `git-legacy-rebase.sh` and introduce a
`builtin/rebase.c` that simply executes the shell script version,
unless the config setting `rebase.useBuiltin` is set to `true`.

The motivation behind this is to rewrite all the functionality of the
shell script version in the aforementioned `rebase.c`, one by one and
be able to conveniently test new features by configuring
`rebase.useBuiltin`.

In the original difftool conversion, if sane_execvp() that attempts to
run the legacy scripted version returned with non-negative status, the
command silently exited without doing anything with success, but
sane_execvp() should not return with non-negative status in the first
place, so we use die() to notice such an abnormal case.

We intentionally avoid reading the config directly to avoid
messing up the GIT_* environment variables when we need to fall back to
exec()ing the shell script. The test of builtin rebase can be done by
`git -c rebase.useBuiltin=true rebase ...`

Signed-off-by: Pratik Karki 
---
 .gitignore|  1 +
 Makefile  |  3 +-
 builtin.h |  1 +
 builtin/rebase.c  | 58 +++
 git-rebase.sh => git-legacy-rebase.sh |  0
 git.c |  6 +++
 6 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (100%)

diff --git a/.gitignore b/.gitignore
index 3284a1e9b1..ec23959014 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-legacy-rebase
 /git-log
 /git-ls-files
 /git-ls-remote
diff --git a/Makefile b/Makefile
index 08e5c54549..a0c410b7d6 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,7 @@ 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-legacy-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -1059,6 +1059,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/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index 0362f1ce25..44651a447f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -202,6 +202,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_rebase__helper(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);
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 00..c44addb2a4
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,58 @@
+/*
+ * "git rebase" builtin command
+ *
+ * Copyright (c) 2018 Pratik Karki
+ */
+
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "argv-array.h"
+#include "dir.h"
+
+static int use_builtin_rebase(void)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+   struct strbuf out = STRBUF_INIT;
+   int ret;
+
+   argv_array_pushl(,
+"config", "--bool", "rebase.usebuiltin", NULL);
+   cp.git_cmd = 1;
+   if (capture_command(, , 6)) {
+   strbuf_release();
+   return 0;
+   }
+
+   strbuf_trim();
+   ret = !strcmp("true", out.buf);
+   strbuf_release();
+   return ret;
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+   /*
+* NEEDSWORK: Once the builtin rebase has been tested enough
+* and git-legacy-rebase.sh is retired to contrib/, this preamble
+* can be removed.
+*/
+
+   if (!use_builtin_rebase()) {
+   const char *path = mkpath("%s/git-legacy-rebase",
+ git_exec_path());
+
+   if (sane_execvp(path, (char **)argv) < 0)
+   die_errno(_("could not exec %s"), path);
+   else
+   BUG("sane_execvp() returned???");
+   }
+
+   if (argc != 2)
+   die(_("Usage: %s "), argv[0]);
+ 

[GSoC] [PATCH v5 0/3] rebase: rewrite rebase in C

2018-07-30 Thread Pratik Karki
As a GSoC project, I have been working on the builtin rebase.

The motivation behind the rewrite of rebase i.e. from shell script to C
are for following reasons:

1.  Writing shell scripts and getting it to production is much faster
than doing the equivalent in C but lacks in performance and extra
workarounds are needed for non-POSIX platforms.

2.  Git for Windows is at loss as the installer size increases due to
addition of extra dependencies for the shell scripts which are usually
available in POSIX compliant platforms.

This series of patches serves to demonstrate a minimal builtin rebase
which supports running `git rebase ` and also serves to ask for
reviews.

Changes since v4:

  -  Remove the `do_reset()` refactored function from sequencer.
 In other words `sequencer: refactor the code to detach HEAD to checkout.c`
 patch was dropped to introduce a new function `reset_hard()` for `rebase.c`
 (as suggested by Johannes).

  -  Fix a case of leak in `rebase: start implementing it as a builtin`.
 (as pointed out by Andrei Rybak and Eric Sunshine).

  -  Wrap the user visible comments in `_()` and used `BUG()` depending on the
 scenarios (as pointed out by Duy Nguyen).

  -  Fix the macro `GIT_PATH_FUNC` which expands to function definition and
 doesn't require semicolons (as pointed out by Beat Bolli).

Pratik Karki (3):
  rebase: start implementing it as a builtin
  rebase: refactor common shell functions into their own file
  builtin/rebase: support running "git rebase "

 .gitignore|   2 +
 Makefile  |   4 +-
 builtin.h |   1 +
 builtin/rebase.c  | 406 ++
 git-rebase.sh => git-legacy-rebase.sh |  69 +
 git-rebase--common.sh |  68 +
 git.c |   6 +
 7 files changed, 488 insertions(+), 68 deletions(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (90%)
 create mode 100644 git-rebase--common.sh

-- 
2.18.0



Re: [PATCH] fixup! builtin/rebase: support running "git rebase "

2018-07-21 Thread Pratik Karki
Hi,

On Sat, Jul 21, 2018 at 6:36 PM Beat Bolli  wrote:
>
> The macro GIT_PATH_FUNC expands to a function definition that ends with
> a closing brace. The extra semicolon produces a warning when compiling
> with -pedantic.
>
> Signed-off-by: Beat Bolli 
> ---
>
> Junio, this applies on top of pk/rebase-in-c.
>
> Thanks, Beat
>
>  builtin/rebase.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index e38ea80874..6aaae2436f 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -14,8 +14,8 @@
>  #include "refs.h"
>  #include "quote.h"
>
> -static GIT_PATH_FUNC(apply_dir, "rebase-apply");
> -static GIT_PATH_FUNC(merge_dir, "rebase-merge");
> +static GIT_PATH_FUNC(apply_dir, "rebase-apply")
> +static GIT_PATH_FUNC(merge_dir, "rebase-merge")
>

First of all, I apologize for not replying earlier to your review.
I have already fixed this macro in my local repository.

The new iteration of my patches was being held due to fixup of
`detach_head_to()` and I will soon submit a new iteration of patch
within a day or two.
Thank you for pointing this out.

Cheers,
Pratik Karki


Re: [GSoC] GSoC with git, week 10

2018-07-11 Thread Pratik Karki
Hi,

On Tue, Jul 10, 2018 at 10:05 PM Alban Gruin  wrote:
>
> Hi,
>
> I published a new blog post:
>
> https://blog.pa1ch.fr/posts/2018/07/10/en/gsoc2018-week-10.html
>
Nice work.

Sorry for late notification, my blog is up too.

https://prertik.github.io/post/week-10

Cheers,
Pratik


Re: [PATCH v4 3/4] sequencer: refactor the code to detach HEAD to checkout.c

2018-07-09 Thread Pratik Karki
Hi,
On Mon, Jul 9, 2018 at 3:16 AM Johannes Schindelin
 wrote:
>
> Hi Pratik,
>
> On Sun, 8 Jul 2018, Pratik Karki wrote:
>
> > diff --git a/checkout.c b/checkout.c
> > index bdefc888ba..da68915fd7 100644
> > --- a/checkout.c
> > +++ b/checkout.c
> > @@ -2,6 +2,11 @@
> >  #include "remote.h"
> >  #include "refspec.h"
> >  #include "checkout.h"
> > +#include "unpack-trees.h"
> > +#include "lockfile.h"
> > +#include "refs.h"
> > +#include "tree.h"
> > +#include "cache-tree.h"
> >
> >  struct tracking_name_data {
> >   /* const */ char *src_ref;
> > @@ -42,3 +47,62 @@ const char *unique_tracking_name(const char *name, 
> > struct object_id *oid)
> >   free(cb_data.dst_ref);
> >   return NULL;
> >  }
> > +
> > +int detach_head_to(struct object_id *oid, const char *action,
> > +const char *reflog_message)
> > +{
> > + struct strbuf ref_name = STRBUF_INIT;
> > + struct tree_desc desc;
> > + struct lock_file lock = LOCK_INIT;
> > + struct unpack_trees_options unpack_tree_opts;
> > + struct tree *tree;
> > + int ret = 0;
> > +
> > + if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
> > + return -1;
> > +
> > + memset(_tree_opts, 0, sizeof(unpack_tree_opts));
> > + setup_unpack_trees_porcelain(_tree_opts, action);
> > + unpack_tree_opts.head_idx = 1;
> > + unpack_tree_opts.src_index = _index;
> > + unpack_tree_opts.dst_index = _index;
> > + unpack_tree_opts.fn = oneway_merge;
> > + unpack_tree_opts.merge = 1;
> > + unpack_tree_opts.update = 1;
> > +
> > + if (read_cache_unmerged()) {
> > + rollback_lock_file();
> > + strbuf_release(_name);
> > + return error_resolve_conflict(_(action));
> > + }
> > +
> > + if (!fill_tree_descriptor(, oid)) {
> > + error(_("failed to find tree of %s"), oid_to_hex(oid));
> > + rollback_lock_file();
> > + free((void *)desc.buffer);
> > + strbuf_release(_name);
> > + return -1;
> > + }
> > +
> > + if (unpack_trees(1, , _tree_opts)) {
> > + rollback_lock_file();
> > + free((void *)desc.buffer);
> > + strbuf_release(_name);
> > + return -1;
> > + }
> > +
> > + tree = parse_tree_indirect(oid);
> > + prime_cache_tree(_index, tree);
> > +
> > + if (write_locked_index(_index, , COMMIT_LOCK) < 0)
> > + ret = error(_("could not write index"));
> > + free((void *)desc.buffer);
> > +
> > + if (!ret)
> > + ret = update_ref(reflog_message, "HEAD", oid,
> > +  NULL, 0, UPDATE_REFS_MSG_ON_ERR);
>
> I noticed that this does not actually detach the HEAD. That is my fault,
> of course, as I should have not only suggested refactoring the
> `do_reset()` function from `sequencer.c`, but I should also have
> remembered that that function has the benefit of *always* acting on a
> detached HEAD (because it runs during an interactive rebase), and
> therefore does not need to detach it explicitly.
>
> In light of the `reset_hard()` function that you added in a `wip` (see
> https://github.com/git/git/pull/505/files#diff-c7361e406139e8cd3a300b80b8f8cc8dR296),
> I could imagine that it might be better, after all, to leave `do_reset()`
> alone and implement a `reset_hard()` function that also optionally
> detaches the `HEAD` (I *think* that the flag `REF_NO_DEREF` would do that
> for you).

Yes. I think this will be better. Thanks.

> Alternatively, just update the code in `do_reset()` to use that flag
> first, and only *then* extract the code to `checkout.c`.
>
> (I could not resist, and made this quick change on top of your
> `wip-rebase`, and together with a couple more, obvious fixups, this lets
> t3403 pass. It still needs some things that you have not yet sent to the
> mailing list, such as support for `--skip`.)

Thank you for taking the time to review.


Re: [PATCH v4 3/4] sequencer: refactor the code to detach HEAD to checkout.c

2018-07-09 Thread Pratik Karki
On Mon, Jul 9, 2018 at 10:27 PM Junio C Hamano  wrote:
>
> Pratik Karki  writes:
>
> > In the upcoming builtin rebase, we will have to start by detaching
> > the HEAD, just like shell script version does. Essentially, we have
> > to do the same thing as `git checkout -q ^0 --`, in pure C.
> >
> > The aforementioned functionality was already present in `sequencer.c`
> > in `do_reset()` function. But `do_reset()` performs more than detaching
> > the HEAD, and performs action specific to `sequencer.c`.
> >
> > So this commit refactors out that part from `do_reset()`, and moves it
> > to a new function called `detach_head_to()`. As this function has
> > nothing to do with the sequencer, and everything to do with what `git
> > checkout -q ^0 --` does, we move that function to checkout.c.
> >
> > This refactoring actually introduces a slight change in behavior:
> > previously, the index was locked before parsing the argument to the
> > todo command `reset`, while it now gets locked *after* that, in the
> > `detach_head_to()` function.
> >
> > It does not make a huge difference, and the upside is that this closes
> > a few (unlikely) code paths where the index would not be unlocked upon
> > error.
> >
> > Signed-off-by: Pratik Karki 
> > ---
>
> Here is a place to say "unchanged since v3" for a change like this
> one that changes neither the proposed log message above nor the
> patch below to help reviewers who have seen the previous round (they
> can stop reading here).  Hopefully there are more reviewers than you
> who write new code, so spending a bit more time to help them spend
> less would be an overall win for the project.

Thanks for the review. I'll keep this in mind, the next time.


Re: [PATCH v4 1/4] rebase: start implementing it as a builtin

2018-07-09 Thread Pratik Karki
Hi,

On Mon, Jul 9, 2018 at 2:21 PM Eric Sunshine  wrote:
>
> On Mon, Jul 9, 2018 at 3:59 AM Andrei Rybak  wrote:
> > On 2018-07-08 20:01, Pratik Karki wrote:
> > > +static int use_builtin_rebase(void)
> > > +{
> > > + struct child_process cp = CHILD_PROCESS_INIT;
> > > + struct strbuf out = STRBUF_INIT;
> > > + int ret;
> > > +
> > > + argv_array_pushl(,
> > > +  "config", "--bool", "rebase.usebuiltin", NULL);
> > > + cp.git_cmd = 1;
> > > + if (capture_command(, , 6))
> > > + return 0;
> >
> > Does strbuf out leak on return here?
>
> Good catch. This _is_ a potential leak. Here is an excerpt from the
> documentation of pipe_command(), which is called by capture_command():
>
> Any output collected in the buffers is kept even if the
> command returns a non-zero exit.
>
> So, yes, this needs a strbuf_release() before returning.

Hmm. This seems to be a problem. Thanks for reviewing.


[PATCH v4 4/4] builtin/rebase: support running "git rebase "

2018-07-08 Thread Pratik Karki
This patch gives life to the skeleton added in the previous patches:
With this change, we can perform a elementary rebase (without any
options).

It can be tested thusly by:

git -c rebase.usebuiltin=true rebase HEAD~2

The rebase backends (i.e. the shell script functions defined in
`git-rebase--`) are still at work here and the "builtin
rebase"'s purpose is simply to parse the options and set
everything up so that those rebase backends can do their work.

Note: We take an alternative approach here which is *not* to set the
environment variables via `run_command_v_opt_cd_env()` because those
variables would then be visible by processes spawned from the rebase
backends. Instead, we work hard to set them in the shell script snippet.

The next commits will handle and support all the wonderful rebase
options.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 238 ++-
 1 file changed, 237 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fab7fca37e..e38ea80874 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -9,6 +9,20 @@
 #include "exec-cmd.h"
 #include "argv-array.h"
 #include "dir.h"
+#include "packfile.h"
+#include "checkout.h"
+#include "refs.h"
+#include "quote.h"
+
+static GIT_PATH_FUNC(apply_dir, "rebase-apply");
+static GIT_PATH_FUNC(merge_dir, "rebase-merge");
+
+enum rebase_type {
+   REBASE_AM,
+   REBASE_MERGE,
+   REBASE_INTERACTIVE,
+   REBASE_PRESERVE_MERGES
+};
 
 static int use_builtin_rebase(void)
 {
@@ -28,8 +42,136 @@ static int use_builtin_rebase(void)
return ret;
 }
 
+static int apply_autostash(void)
+{
+   warning("TODO");
+   return 0;
+}
+
+struct rebase_options {
+   enum rebase_type type;
+   const char *state_dir;
+   struct commit *upstream;
+   const char *upstream_name;
+   char *head_name;
+   struct object_id orig_head;
+   struct commit *onto;
+   const char *onto_name;
+   const char *revisions;
+   const char *root;
+};
+
+static int finish_rebase(struct rebase_options *opts)
+{
+   struct strbuf dir = STRBUF_INIT;
+   const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+
+   delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+   apply_autostash();
+   close_all_packs(the_repository->objects);
+   /*
+* We ignore errors in 'gc --auto', since the
+* user should see them.
+*/
+   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+   strbuf_addstr(, opts->state_dir);
+   remove_dir_recursively(, 0);
+   strbuf_release();
+
+   return 0;
+}
+
+static struct commit *peel_committish(const char *name)
+{
+   struct object *obj;
+   struct object_id oid;
+
+   if (get_oid(name, ))
+   return NULL;
+   obj = parse_object();
+   return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
+static void add_var(struct strbuf *buf, const char *name, const char *value)
+{
+   strbuf_addstr(buf, name);
+   strbuf_addstr(buf, "=");
+   sq_quote_buf(buf, value);
+   strbuf_addstr(buf, "; ");
+}
+
+static int run_specific_rebase(struct rebase_options *opts)
+{
+   const char *argv[] = { NULL, NULL };
+   struct strbuf script_snippet = STRBUF_INIT;
+   int status;
+   const char *backend, *backend_func;
+
+   add_var(_snippet, "GIT_DIR", absolute_path(get_git_dir()));
+
+   add_var(_snippet, "upstream_name", opts->upstream_name);
+   add_var(_snippet, "upstream",
+oid_to_hex(>upstream->object.oid));
+   add_var(_snippet, "head_name", opts->head_name);
+   add_var(_snippet, "orig_head", oid_to_hex(>orig_head));
+   add_var(_snippet, "onto", oid_to_hex(>onto->object.oid));
+   add_var(_snippet, "onto_name", opts->onto_name);
+   add_var(_snippet, "revisions", opts->revisions);
+
+   switch (opts->type) {
+   case REBASE_AM:
+   backend = "git-rebase--am";
+   backend_func = "git_rebase__am";
+   break;
+   case REBASE_INTERACTIVE:
+   backend = "git-rebase--interactive";
+   backend_func = "git_rebase__interactive";
+   break;
+   case REBASE_MERGE:
+   backend = "git-rebase--merge";
+   backend_func = "git_rebase__merge";
+   break;
+   case REBASE_PRESERVE_MERGES:
+   backend = "git-rebase--preserve-merges";
+   backend_func = "git_rebase__preserve_merges";
+   break;
+   default:
+   BUG("Unha

[PATCH v4 3/4] sequencer: refactor the code to detach HEAD to checkout.c

2018-07-08 Thread Pratik Karki
In the upcoming builtin rebase, we will have to start by detaching
the HEAD, just like shell script version does. Essentially, we have
to do the same thing as `git checkout -q ^0 --`, in pure C.

The aforementioned functionality was already present in `sequencer.c`
in `do_reset()` function. But `do_reset()` performs more than detaching
the HEAD, and performs action specific to `sequencer.c`.

So this commit refactors out that part from `do_reset()`, and moves it
to a new function called `detach_head_to()`. As this function has
nothing to do with the sequencer, and everything to do with what `git
checkout -q ^0 --` does, we move that function to checkout.c.

This refactoring actually introduces a slight change in behavior:
previously, the index was locked before parsing the argument to the
todo command `reset`, while it now gets locked *after* that, in the
`detach_head_to()` function.

It does not make a huge difference, and the upside is that this closes
a few (unlikely) code paths where the index would not be unlocked upon
error.

Signed-off-by: Pratik Karki 
---
 checkout.c  | 64 +
 checkout.h  |  3 +++
 sequencer.c | 58 +---
 3 files changed, 72 insertions(+), 53 deletions(-)

diff --git a/checkout.c b/checkout.c
index bdefc888ba..da68915fd7 100644
--- a/checkout.c
+++ b/checkout.c
@@ -2,6 +2,11 @@
 #include "remote.h"
 #include "refspec.h"
 #include "checkout.h"
+#include "unpack-trees.h"
+#include "lockfile.h"
+#include "refs.h"
+#include "tree.h"
+#include "cache-tree.h"
 
 struct tracking_name_data {
/* const */ char *src_ref;
@@ -42,3 +47,62 @@ const char *unique_tracking_name(const char *name, struct 
object_id *oid)
free(cb_data.dst_ref);
return NULL;
 }
+
+int detach_head_to(struct object_id *oid, const char *action,
+  const char *reflog_message)
+{
+   struct strbuf ref_name = STRBUF_INIT;
+   struct tree_desc desc;
+   struct lock_file lock = LOCK_INIT;
+   struct unpack_trees_options unpack_tree_opts;
+   struct tree *tree;
+   int ret = 0;
+
+   if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
+   return -1;
+
+   memset(_tree_opts, 0, sizeof(unpack_tree_opts));
+   setup_unpack_trees_porcelain(_tree_opts, action);
+   unpack_tree_opts.head_idx = 1;
+   unpack_tree_opts.src_index = _index;
+   unpack_tree_opts.dst_index = _index;
+   unpack_tree_opts.fn = oneway_merge;
+   unpack_tree_opts.merge = 1;
+   unpack_tree_opts.update = 1;
+
+   if (read_cache_unmerged()) {
+   rollback_lock_file();
+   strbuf_release(_name);
+   return error_resolve_conflict(_(action));
+   }
+
+   if (!fill_tree_descriptor(, oid)) {
+   error(_("failed to find tree of %s"), oid_to_hex(oid));
+   rollback_lock_file();
+   free((void *)desc.buffer);
+   strbuf_release(_name);
+   return -1;
+   }
+
+   if (unpack_trees(1, , _tree_opts)) {
+   rollback_lock_file();
+   free((void *)desc.buffer);
+   strbuf_release(_name);
+   return -1;
+   }
+
+   tree = parse_tree_indirect(oid);
+   prime_cache_tree(_index, tree);
+
+   if (write_locked_index(_index, , COMMIT_LOCK) < 0)
+   ret = error(_("could not write index"));
+   free((void *)desc.buffer);
+
+   if (!ret)
+   ret = update_ref(reflog_message, "HEAD", oid,
+NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+
+   strbuf_release(_name);
+   return ret;
+
+}
diff --git a/checkout.h b/checkout.h
index 9980711179..6ce46cf4e8 100644
--- a/checkout.h
+++ b/checkout.h
@@ -10,4 +10,7 @@
  */
 extern const char *unique_tracking_name(const char *name, struct object_id 
*oid);
 
+int detach_head_to(struct object_id *oid, const char *action,
+  const char *reflog_message);
+
 #endif /* CHECKOUT_H */
diff --git a/sequencer.c b/sequencer.c
index 5354d4d51e..106d518f4d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -29,6 +29,7 @@
 #include "oidset.h"
 #include "commit-slab.h"
 #include "alias.h"
+#include "checkout.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -2756,14 +2757,7 @@ static int do_reset(const char *name, int len, struct 
replay_opts *opts)
 {
struct strbuf ref_name = STRBUF_INIT;
struct object_id oid;
-   struct lock_file lock = LOCK_INIT;
-   struct tree_desc desc;
-   struct tree *tree;
-   struct unpack_trees_options unpack_tree_opts;
-   int ret = 0, i;
-
-   if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
-   return -1;
+   int i;
 
if (len == 10 && !st

[PATCH v4 2/4] rebase: refactor common shell functions into their own file

2018-07-08 Thread Pratik Karki
The functions present in `git-legacy-rebase.sh` are used by the rebase
backends as they are implemented as shell script functions in the
`git-rebase--` files.

To make the `builtin/rebase.c` work, we have to provide support via
a Unix shell script snippet that uses these functions and so, we
want to use the rebase backends *directly* from the builtin rebase
without going through `git-legacy-rebase.sh`.

This commit extracts the functions to a separate file,
`git-rebase--common`, that will be read by `git-legacy-rebase.sh` and
by the shell script snippets which will be used extensively in the
following commits.

Signed-off-by: Pratik Karki 
---
 .gitignore|  1 +
 Makefile  |  1 +
 git-legacy-rebase.sh  | 69 ++-
 git-rebase--common.sh | 68 ++
 4 files changed, 72 insertions(+), 67 deletions(-)
 create mode 100644 git-rebase--common.sh

diff --git a/.gitignore b/.gitignore
index ec23959014..824141cba1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@
 /git-read-tree
 /git-rebase
 /git-rebase--am
+/git-rebase--common
 /git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
diff --git a/Makefile b/Makefile
index e88fe2e5fb..163e52ad1a 100644
--- a/Makefile
+++ b/Makefile
@@ -619,6 +619,7 @@ 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--common
 SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
diff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh
index 19bdebb480..fbaf16420d 100755
--- a/git-legacy-rebase.sh
+++ b/git-legacy-rebase.sh
@@ -57,12 +57,7 @@ cd_to_toplevel
 LF='
 '
 ok_to_skip_pre_rebase=
-resolvemsg="
-$(gettext 'Resolve all conflicts manually, mark them as resolved with
-"git add/rm ", then run "git rebase --continue".
-You can instead skip this commit: run "git rebase --skip".
-To abort and get back to the state before "git rebase", run "git rebase 
--abort".')
-"
+
 squash_onto=
 unset onto
 unset restrict_revision
@@ -102,6 +97,7 @@ case "$(git config --bool commit.gpgsign)" in
 true)  gpg_sign_opt=-S ;;
 *) gpg_sign_opt= ;;
 esac
+. git-rebase--common
 
 read_basic_state () {
test -f "$state_dir/head-name" &&
@@ -132,67 +128,6 @@ read_basic_state () {
}
 }
 
-write_basic_state () {
-   echo "$head_name" > "$state_dir"/head-name &&
-   echo "$onto" > "$state_dir"/onto &&
-   echo "$orig_head" > "$state_dir"/orig-head &&
-   echo "$GIT_QUIET" > "$state_dir"/quiet &&
-   test t = "$verbose" && : > "$state_dir"/verbose
-   test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
-   test -n "$strategy_opts" && echo "$strategy_opts" > \
-   "$state_dir"/strategy_opts
-   test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > 
\
-   "$state_dir"/allow_rerere_autoupdate
-   test -n "$gpg_sign_opt" && echo "$gpg_sign_opt" > 
"$state_dir"/gpg_sign_opt
-   test -n "$signoff" && echo "$signoff" >"$state_dir"/signoff
-}
-
-output () {
-   case "$verbose" in
-   '')
-   output=$("$@" 2>&1 )
-   status=$?
-   test $status != 0 && printf "%s\n" "$output"
-   return $status
-   ;;
-   *)
-   "$@"
-   ;;
-   esac
-}
-
-move_to_original_branch () {
-   case "$head_name" in
-   refs/*)
-   message="rebase finished: $head_name onto $onto"
-   git update-ref -m "$message" \
-   $head_name $(git rev-parse HEAD) $orig_head &&
-   git symbolic-ref \
-   -m "rebase finished: returning to $head_name" \
-   HEAD $head_name ||
-   die "$(eval_gettext "Could not move back to \$head_name")"
-   ;;
-   esac
-}
-
-apply_autostash () {
-   if test -f "$state_dir/autostash"
-   then
-   stash_sha1=$(cat "$state_dir/autostash")
-   if git stash apply $stash_sha1 >/dev/null 2>&1
-   then
-   echo "$(gettext 'Applied autostash.')" >&2
-   else
-   git stash store -m "autostash"

[PATCH v4 1/4] rebase: start implementing it as a builtin

2018-07-08 Thread Pratik Karki
This commit imitates the strategy that was used to convert the
difftool to a builtin. We start by renaming the shell script
`git-rebase.sh` to `git-legacy-rebase.sh` and introduce a
`builtin/rebase.c` that simply executes the shell script version,
unless the config setting `rebase.useBuiltin` is set to `true`.

The motivation behind this is to rewrite all the functionality of the
shell script version in the aforementioned `rebase.c`, one by one and
be able to conveniently test new features by configuring
`rebase.useBuiltin`.

In the original difftool conversion, if sane_execvp() that attempts to
run the legacy scripted version returned with non-negative status, the
command silently exited without doing anything with success, but
sane_execvp() should not retun with non-negative status in the first
place, so we use die() to notice such an abnormal case.

We intentionally avoid reading the config directly to avoid
messing up the GIT_* environment variables when we need to fall back to
exec()ing the shell script. The test of builtin rebase can be done by
`git -c rebase.useBuiltin=true rebase ...`

Signed-off-by: Pratik Karki 
---
 .gitignore|  1 +
 Makefile  |  3 +-
 builtin.h |  1 +
 builtin/rebase.c  | 56 +++
 git-rebase.sh => git-legacy-rebase.sh |  0
 git.c |  6 +++
 6 files changed, 66 insertions(+), 1 deletion(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (100%)

diff --git a/.gitignore b/.gitignore
index 3284a1e9b1..ec23959014 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-legacy-rebase
 /git-log
 /git-ls-files
 /git-ls-remote
diff --git a/Makefile b/Makefile
index 0cb6590f24..e88fe2e5fb 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,7 @@ 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-legacy-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -1059,6 +1059,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/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index 0362f1ce25..44651a447f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -202,6 +202,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_rebase__helper(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);
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 00..fab7fca37e
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,56 @@
+/*
+ * "git rebase" builtin command
+ *
+ * Copyright (c) 2018 Pratik Karki
+ */
+
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "argv-array.h"
+#include "dir.h"
+
+static int use_builtin_rebase(void)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+   struct strbuf out = STRBUF_INIT;
+   int ret;
+
+   argv_array_pushl(,
+"config", "--bool", "rebase.usebuiltin", NULL);
+   cp.git_cmd = 1;
+   if (capture_command(, , 6))
+   return 0;
+
+   strbuf_trim();
+   ret = !strcmp("true", out.buf);
+   strbuf_release();
+   return ret;
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+   /*
+* NEEDSWORK: Once the builtin rebase has been tested enough
+* and git-legacy-rebase.sh is retired to contrib/, this preamble
+* can be removed.
+*/
+
+   if (!use_builtin_rebase()) {
+   const char *path = mkpath("%s/git-legacy-rebase",
+ git_exec_path());
+
+   if (sane_execvp(path, (char **)argv) < 0)
+   die_errno("could not exec %s", path);
+   else
+   die("sane_execvp() returned???");
+   }
+
+   if (argc != 2)
+   die("Usage: %s ", argv[0]);
+   prefix = setup_git_directory();
+   trace_repo_se

[GSoC] [PATCH v4 0/4] rebase: rewrite rebase in C

2018-07-08 Thread Pratik Karki
As a GSoC project, I have been working on the builtin rebase.

The motivation behind the rewrite of rebase i.e. from shell script to C
are for following reasons:

1.  Writing shell scripts and getting it to production is much faster
than doing the equivalent in C but lacks in performance and extra
workarounds are needed for non-POSIX platforms.

2.  Git for Windows is at loss as the installer size increases due to
addition of extra dependencies for the shell scripts which are usually
available in POSIX compliant platforms.

This series of patches serves to demonstrate a minimal builtin rebase
which supports running `git rebase ` and also serves to ask for
reviews.

Changes since v3:

  -  Fix commit message of `rebase: start implementing it as a builtin`.

  -  Acknowledge Junio's style reviews.

  -  Acknowledge Johannes Schindelin's review.

Pratik Karki (4):
  rebase: start implementing it as a builtin
  rebase: refactor common shell functions into their own file
  sequencer: refactor the code to detach HEAD to checkout.c
  builtin/rebase: support running "git rebase "

 .gitignore|   2 +
 Makefile  |   4 +-
 builtin.h |   1 +
 builtin/rebase.c  | 292 ++
 checkout.c|  64 ++
 checkout.h|   3 +
 git-rebase.sh => git-legacy-rebase.sh |  69 +-
 git-rebase--common.sh |  68 ++
 git.c |   6 +
 sequencer.c   |  58 +
 10 files changed, 446 insertions(+), 121 deletions(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (89%)
 create mode 100644 git-rebase--common.sh

-- 
2.18.0



[PATCH v3 4/4] builtin/rebase: support running "git rebase "

2018-07-06 Thread Pratik Karki
This patch gives life to the skeleton added in the previous patches:
With this change, we can perform a elementary rebase (without any
options).

It can be tested thusly by:

git -c rebase.usebuiltin=true rebase HEAD~2

The rebase backends (i.e. the shell script functions defined in
`git-rebase--`) are still at work here and the "builtin
rebase"'s purpose is simply to parse the options and set
everything up so that those rebase backends can do their work.

Note: We take an alternative approach here which is *not* to set the
environment variables via `run_command_v_opt_cd_env()` because those
variables would then be visible by processes spawned from the rebase
backends. Instead, we work hard to set them in the shell script snippet.

The next commits will handle and support all the wonderful rebase
options.

Signed-off-by: Pratik Karki 
---
 builtin/rebase.c | 237 ++-
 1 file changed, 236 insertions(+), 1 deletion(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fab7fca37e..71f06f0789 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -9,6 +9,20 @@
 #include "exec-cmd.h"
 #include "argv-array.h"
 #include "dir.h"
+#include "packfile.h"
+#include "checkout.h"
+#include "refs.h"
+#include "quote.h"
+
+static GIT_PATH_FUNC(apply_dir, "rebase-apply");
+static GIT_PATH_FUNC(merge_dir, "rebase-merge");
+
+enum rebase_type {
+   REBASE_AM,
+   REBASE_MERGE,
+   REBASE_INTERACTIVE,
+   REBASE_PRESERVE_MERGES
+};
 
 static int use_builtin_rebase(void)
 {
@@ -28,8 +42,136 @@ static int use_builtin_rebase(void)
return ret;
 }
 
+static int apply_autostash(void)
+{
+   warning("TODO");
+   return 0;
+}
+
+struct rebase_options {
+   enum rebase_type type;
+   const char *state_dir;
+   struct commit *upstream;
+   const char *upstream_name;
+   char *head_name;
+   struct object_id orig_head;
+   struct commit *onto;
+   const char *onto_name;
+   const char *revisions;
+   const char *root;
+};
+
+static int finish_rebase(struct rebase_options *opts)
+{
+   struct strbuf dir = STRBUF_INIT;
+   const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+
+   delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+   apply_autostash();
+   close_all_packs(the_repository->objects);
+   /*
+* We ignore errors in 'gc --auto', since the
+* user should see them.
+*/
+   run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+   strbuf_addstr(, opts->state_dir);
+   remove_dir_recursively(, 0);
+   strbuf_release();
+
+   return 0;
+}
+
+static struct commit *peel_committish(const char *name)
+{
+   struct object *obj;
+   struct object_id oid;
+
+   if (get_oid(name, ))
+   return NULL;
+   obj = parse_object();
+   return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
+}
+
+static void add_var(struct strbuf *buf, const char *name, const char *value)
+{
+   strbuf_addstr(buf, name);
+   strbuf_addstr(buf, "=");
+   sq_quote_buf(buf, value);
+   strbuf_addstr(buf, "; ");
+}
+
+static int run_specific_rebase(struct rebase_options *opts)
+{
+   const char *argv[] = { NULL, NULL };
+   struct strbuf script_snippet = STRBUF_INIT;
+   int status;
+   const char *backend, *backend_func;
+
+   add_var(_snippet, "GIT_DIR", absolute_path(get_git_dir()));
+
+   add_var(_snippet, "upstream_name", opts->upstream_name);
+   add_var(_snippet, "upstream",
+oid_to_hex(>upstream->object.oid));
+   add_var(_snippet, "head_name", opts->head_name);
+   add_var(_snippet, "orig_head", oid_to_hex(>orig_head));
+   add_var(_snippet, "onto", oid_to_hex(>onto->object.oid));
+   add_var(_snippet, "onto_name", opts->onto_name);
+   add_var(_snippet, "revisions", opts->revisions);
+
+   switch (opts->type) {
+   case REBASE_AM:
+   backend = "git-rebase--am";
+   backend_func = "git_rebase__am";
+   break;
+   case REBASE_INTERACTIVE:
+   backend = "git-rebase--interactive";
+   backend_func = "git_rebase__interactive";
+   break;
+   case REBASE_MERGE:
+   backend = "git-rebase--merge";
+   backend_func = "git_rebase__merge";
+   break;
+   case REBASE_PRESERVE_MERGES:
+   backend = "git-rebase--preserve-merges";
+   backend_func = "git_rebase__preserve_merges";
+   break;
+   default:
+   BUG("Unha

[PATCH v3 3/4] sequencer: refactor the code to detach HEAD to checkout.c

2018-07-06 Thread Pratik Karki
In the upcoming builtin rebase, we will have to start by detaching
the HEAD, just like shell script version does. Essentially, we have
to do the same thing as `git checkout -q ^0 --`, in pure C.

The aforementioned functionality was already present in `sequencer.c`
in `do_reset()` function. But `do_reset()` performs more than detaching
the HEAD, and performs action specific to `sequencer.c`.

So this commit refactors out that part from `do_reset()`, and moves it
to a new function called `detach_head_to()`. As this function has
nothing to do with the sequencer, and everything to do with what `git
checkout -q ^0 --` does, we move that function to checkout.c.

This refactoring actually introduces a slight change in behavior:
previously, the index was locked before parsing the argument to the
todo command `reset`, while it now gets locked *after* that, in the
`detach_head_to()` function.

It does not make a huge difference, and the upside is that this closes
a few (unlikely) code paths where the index would not be unlocked upon
error.

Signed-off-by: Pratik Karki 
---
 checkout.c  | 64 +
 checkout.h  |  3 +++
 sequencer.c | 58 +---
 3 files changed, 72 insertions(+), 53 deletions(-)

diff --git a/checkout.c b/checkout.c
index bdefc888ba..da68915fd7 100644
--- a/checkout.c
+++ b/checkout.c
@@ -2,6 +2,11 @@
 #include "remote.h"
 #include "refspec.h"
 #include "checkout.h"
+#include "unpack-trees.h"
+#include "lockfile.h"
+#include "refs.h"
+#include "tree.h"
+#include "cache-tree.h"
 
 struct tracking_name_data {
/* const */ char *src_ref;
@@ -42,3 +47,62 @@ const char *unique_tracking_name(const char *name, struct 
object_id *oid)
free(cb_data.dst_ref);
return NULL;
 }
+
+int detach_head_to(struct object_id *oid, const char *action,
+  const char *reflog_message)
+{
+   struct strbuf ref_name = STRBUF_INIT;
+   struct tree_desc desc;
+   struct lock_file lock = LOCK_INIT;
+   struct unpack_trees_options unpack_tree_opts;
+   struct tree *tree;
+   int ret = 0;
+
+   if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
+   return -1;
+
+   memset(_tree_opts, 0, sizeof(unpack_tree_opts));
+   setup_unpack_trees_porcelain(_tree_opts, action);
+   unpack_tree_opts.head_idx = 1;
+   unpack_tree_opts.src_index = _index;
+   unpack_tree_opts.dst_index = _index;
+   unpack_tree_opts.fn = oneway_merge;
+   unpack_tree_opts.merge = 1;
+   unpack_tree_opts.update = 1;
+
+   if (read_cache_unmerged()) {
+   rollback_lock_file();
+   strbuf_release(_name);
+   return error_resolve_conflict(_(action));
+   }
+
+   if (!fill_tree_descriptor(, oid)) {
+   error(_("failed to find tree of %s"), oid_to_hex(oid));
+   rollback_lock_file();
+   free((void *)desc.buffer);
+   strbuf_release(_name);
+   return -1;
+   }
+
+   if (unpack_trees(1, , _tree_opts)) {
+   rollback_lock_file();
+   free((void *)desc.buffer);
+   strbuf_release(_name);
+   return -1;
+   }
+
+   tree = parse_tree_indirect(oid);
+   prime_cache_tree(_index, tree);
+
+   if (write_locked_index(_index, , COMMIT_LOCK) < 0)
+   ret = error(_("could not write index"));
+   free((void *)desc.buffer);
+
+   if (!ret)
+   ret = update_ref(reflog_message, "HEAD", oid,
+NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+
+   strbuf_release(_name);
+   return ret;
+
+}
diff --git a/checkout.h b/checkout.h
index 9980711179..6ce46cf4e8 100644
--- a/checkout.h
+++ b/checkout.h
@@ -10,4 +10,7 @@
  */
 extern const char *unique_tracking_name(const char *name, struct object_id 
*oid);
 
+int detach_head_to(struct object_id *oid, const char *action,
+  const char *reflog_message);
+
 #endif /* CHECKOUT_H */
diff --git a/sequencer.c b/sequencer.c
index 5354d4d51e..106d518f4d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -29,6 +29,7 @@
 #include "oidset.h"
 #include "commit-slab.h"
 #include "alias.h"
+#include "checkout.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -2756,14 +2757,7 @@ static int do_reset(const char *name, int len, struct 
replay_opts *opts)
 {
struct strbuf ref_name = STRBUF_INIT;
struct object_id oid;
-   struct lock_file lock = LOCK_INIT;
-   struct tree_desc desc;
-   struct tree *tree;
-   struct unpack_trees_options unpack_tree_opts;
-   int ret = 0, i;
-
-   if (hold_locked_index(, LOCK_REPORT_ON_ERROR) < 0)
-   return -1;
+   int i;
 
if (len == 10 && !st

[PATCH v3 1/4] rebase: start implementing it as a builtin

2018-07-06 Thread Pratik Karki
This commit imitates the strategy that was used to convert the
difftool to a builtin. We start by renaming the shell script
`git-rebase.sh` to `git-legacy-rebase.sh` and introduce a
`builtin/rebase.c` that simply executes the shell script version,
unless the config setting `rebase.useBuiltin` is set to `true`.

The motivation behind this is to rewrite all the functionality of the
shell script version in the aforementioned `rebase.c`, one by one and
be able to conveniently test new features by configuring
`rebase.useBuiltin`.

We intentionally avoid reading the config directly to avoid
messing up the GIT_* environment variables when we need to fall back to
exec()ing the shell script. The test of builtin rebase can be done by
`git -c rebase.useBuiltin=true rebase ...`

Signed-off-by: Pratik Karki 
---
 .gitignore|  1 +
 Makefile  |  3 +-
 builtin.h |  1 +
 builtin/rebase.c  | 56 +++
 git-rebase.sh => git-legacy-rebase.sh |  0
 git.c |  6 +++
 6 files changed, 66 insertions(+), 1 deletion(-)
 create mode 100644 builtin/rebase.c
 rename git-rebase.sh => git-legacy-rebase.sh (100%)

diff --git a/.gitignore b/.gitignore
index 3284a1e9b1..ec23959014 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-legacy-rebase
 /git-log
 /git-ls-files
 /git-ls-remote
diff --git a/Makefile b/Makefile
index 0cb6590f24..e88fe2e5fb 100644
--- a/Makefile
+++ b/Makefile
@@ -609,7 +609,7 @@ 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-legacy-rebase.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -1059,6 +1059,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/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index 0362f1ce25..44651a447f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -202,6 +202,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_rebase__helper(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);
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 00..fab7fca37e
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,56 @@
+/*
+ * "git rebase" builtin command
+ *
+ * Copyright (c) 2018 Pratik Karki
+ */
+
+#include "builtin.h"
+#include "run-command.h"
+#include "exec-cmd.h"
+#include "argv-array.h"
+#include "dir.h"
+
+static int use_builtin_rebase(void)
+{
+   struct child_process cp = CHILD_PROCESS_INIT;
+   struct strbuf out = STRBUF_INIT;
+   int ret;
+
+   argv_array_pushl(,
+"config", "--bool", "rebase.usebuiltin", NULL);
+   cp.git_cmd = 1;
+   if (capture_command(, , 6))
+   return 0;
+
+   strbuf_trim();
+   ret = !strcmp("true", out.buf);
+   strbuf_release();
+   return ret;
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+   /*
+* NEEDSWORK: Once the builtin rebase has been tested enough
+* and git-legacy-rebase.sh is retired to contrib/, this preamble
+* can be removed.
+*/
+
+   if (!use_builtin_rebase()) {
+   const char *path = mkpath("%s/git-legacy-rebase",
+ git_exec_path());
+
+   if (sane_execvp(path, (char **)argv) < 0)
+   die_errno("could not exec %s", path);
+   else
+   die("sane_execvp() returned???");
+   }
+
+   if (argc != 2)
+   die("Usage: %s ", argv[0]);
+   prefix = setup_git_directory();
+   trace_repo_setup(prefix);
+   setup_work_tree();
+
+   die("TODO");
+}
diff --git a/git-rebase.sh b/git-legacy-rebase.sh
similarity index 100%
rename from git-rebase.sh
rename to git-legacy-rebase.sh
diff --git a/git.c b/git.c
index 9dbe6ffaa7..0e35632464 100644
--- a/git.c
+++ b/git.c
@@ -518,6 +518,12 @@ static struct cm

  1   2   >