[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;
+
+   fd = hold_locked_index(_file, 0);
+   refresh_cache(REFRESH_QUIET);
+   if (0 <= fd)
+   update_index_if_able(_index, _file);
+   rollback_lock_file(_file);
+
+   if (has_unstaged_changes(0) || 

[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;
+
+   commit = lookup_commit(the_repository, oid);
+   if (!commit ||
+   (commit->object.flags & TMP_MARK) ||
+   parse_commit(commit))
+ 

[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 */
+   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 

[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