* [PATCH 5/5] sequencer: revert d3f4628e
From: Ramkumar Ramachandra @ 2011-11-05 16:29 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder, Christian Couder
In-Reply-To: <1320510586-3940-1-git-send-email-artagnon@gmail.com>
Revert d3f4628e (revert: Remove sequencer state when no commits are
pending, 2011-06-06), because this is not the right approach. Instead
of increasing coupling between the sequencer and 'git commit', a
unified '--continue' that invokes 'git commit' on behalf of the
end-user is preferred.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
sequencer.c | 12 +-----------
t/t3510-cherry-pick-sequence.sh | 24 ------------------------
2 files changed, 1 insertions(+), 35 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 6762ceb..7caa550 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -758,18 +758,8 @@ static int pick_commits(struct replay_insn_list *todo_list,
for (cur = todo_list; cur; cur = cur->next) {
save_todo(cur);
res = do_pick_commit(cur->operand, cur->action, opts);
- if (res) {
- if (!cur->next)
- /*
- * An error was encountered while
- * picking the last commit; the
- * sequencer state is useless now --
- * the user simply needs to resolve
- * the conflict and commit
- */
- remove_sequencer_state(0);
+ if (res)
return res;
- }
}
/*
diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh
index 4b12244..b30f13a 100755
--- a/t/t3510-cherry-pick-sequence.sh
+++ b/t/t3510-cherry-pick-sequence.sh
@@ -85,30 +85,6 @@ test_expect_success '--reset cleans up sequencer state' '
test_path_is_missing .git/sequencer
'
-test_expect_success 'cherry-pick cleans up sequencer state when one commit is left' '
- pristine_detach initial &&
- test_must_fail git cherry-pick base..picked &&
- test_path_is_missing .git/sequencer &&
- echo "resolved" >foo &&
- git add foo &&
- git commit &&
- {
- git rev-list HEAD |
- git diff-tree --root --stdin |
- sed "s/$_x40/OBJID/g"
- } >actual &&
- cat >expect <<-\EOF &&
- OBJID
- :100644 100644 OBJID OBJID M foo
- OBJID
- :100644 100644 OBJID OBJID M unrelated
- OBJID
- :000000 100644 OBJID OBJID A foo
- :000000 100644 OBJID OBJID A unrelated
- EOF
- test_cmp expect actual
-'
-
test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
pristine_detach initial &&
test_must_fail git cherry-pick base..anotherpick &&
--
1.7.6.351.gb35ac.dirty
^ permalink raw reply related
* [PATCH 4/5] sequencer: handle single commit pick separately
From: Ramkumar Ramachandra @ 2011-11-05 16:29 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder, Christian Couder
In-Reply-To: <1320510586-3940-1-git-send-email-artagnon@gmail.com>
Don't write a '.git/sequencer/todo', as CHERRY_PICK_HEAD already
contains this information. However, '.git/sequencer/opts' and
'.git/sequencer/head' are required to support '--reset' and
'--continue' operations.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
sequencer.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 517eb23..6762ceb 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -746,6 +746,15 @@ static int pick_commits(struct replay_insn_list *todo_list,
opts->record_origin || opts->edit));
read_and_refresh_cache(opts);
+ /*
+ * Backward compatibility hack: when only a single commit is
+ * picked, don't save_todo(), because CHERRY_PICK_HEAD will
+ * contain this information anyway.
+ */
+ if (opts->subcommand == REPLAY_NONE &&
+ todo_list->next == NULL && todo_list->action == REPLAY_PICK)
+ return do_pick_commit(todo_list->operand, REPLAY_PICK, opts);
+
for (cur = todo_list; cur; cur = cur->next) {
save_todo(cur);
res = do_pick_commit(cur->operand, cur->action, opts);
--
1.7.6.351.gb35ac.dirty
^ permalink raw reply related
* [PATCH 3/5] sequencer: sequencer state is useless without todo
From: Ramkumar Ramachandra @ 2011-11-05 16:29 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder, Christian Couder
In-Reply-To: <1320510586-3940-1-git-send-email-artagnon@gmail.com>
Later in the series, we will not write '.git/sequencer/todo' for a
single commit cherry-pick, because 'CHERRY_PICK_HEAD' already contains
this information. So, stomp the sequencer state in create_seq_state()
unless the todo file is present.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
sequencer.c | 10 +++++++---
1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index e566043..517eb23 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -654,11 +654,15 @@ static void walk_revs_populate_todo(struct replay_insn_list **todo_list,
static int create_seq_dir(void)
{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
const char *seq_dir = git_path(SEQ_DIR);
- if (file_exists(seq_dir))
- return error(_("%s already exists."), seq_dir);
- else if (mkdir(seq_dir, 0777) < 0)
+ if (file_exists(todo_file))
+ return error(_("%s already exists."), todo_file);
+
+ /* If todo_file doesn't exist, discard sequencer state */
+ remove_sequencer_state(1);
+ if (mkdir(seq_dir, 0777) < 0)
die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
return 0;
}
--
1.7.6.351.gb35ac.dirty
^ permalink raw reply related
* [PATCH 2/5] sequencer: remove CHERRY_PICK_HEAD with sequencer state
From: Ramkumar Ramachandra @ 2011-11-05 16:29 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder, Christian Couder
In-Reply-To: <1320510586-3940-1-git-send-email-artagnon@gmail.com>
Make remove_sequencer_state() remove '.git/CHERRY_PICK_HEAD' when
invoked aggressively, since we want to treat it as part of the
sequencer state now. While at it, make some minor improvements to the
function.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
sequencer.c | 27 ++++++++++++++++-----------
1 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/sequencer.c b/sequencer.c
index 87f146b..e566043 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -25,17 +25,22 @@ static char *get_encoding(const char *message);
void remove_sequencer_state(int aggressive)
{
- struct strbuf seq_dir = STRBUF_INIT;
- struct strbuf seq_old_dir = STRBUF_INIT;
-
- strbuf_addf(&seq_dir, "%s", git_path(SEQ_DIR));
- strbuf_addf(&seq_old_dir, "%s", git_path(SEQ_OLD_DIR));
- remove_dir_recursively(&seq_old_dir, 0);
- rename(git_path(SEQ_DIR), git_path(SEQ_OLD_DIR));
- if (aggressive)
- remove_dir_recursively(&seq_old_dir, 0);
- strbuf_release(&seq_dir);
- strbuf_release(&seq_old_dir);
+ const char *seq_dir = git_path(SEQ_DIR);
+ const char *seq_old_dir = git_path(SEQ_OLD_DIR);
+ const char *cherry_pick_head = git_path("CHERRY_PICK_HEAD");
+ struct strbuf seq_dir_buf = STRBUF_INIT;
+ struct strbuf seq_old_dir_buf = STRBUF_INIT;
+
+ strbuf_addf(&seq_dir_buf, "%s", seq_dir);
+ strbuf_addf(&seq_old_dir_buf, "%s", seq_old_dir);
+ remove_dir_recursively(&seq_old_dir_buf, 0);
+ rename(seq_dir, seq_old_dir);
+ if (aggressive) {
+ remove_dir_recursively(&seq_old_dir_buf, 0);
+ unlink(cherry_pick_head);
+ }
+ strbuf_release(&seq_dir_buf);
+ strbuf_release(&seq_old_dir_buf);
}
struct commit_message {
--
1.7.6.351.gb35ac.dirty
^ permalink raw reply related
* [PATCH 1/5] sequencer: factor code out of revert builtin
From: Ramkumar Ramachandra @ 2011-11-05 16:29 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder, Christian Couder
In-Reply-To: <1320510586-3940-1-git-send-email-artagnon@gmail.com>
Start building the generalized sequencer by moving code from revert.c
into sequencer.c and sequencer.h. Make the builtin responsible only
for command-line parsing, and expose a new sequencer_pick_revisions()
to do the actual work of sequencing commits.
This is intended to be almost a pure code movement patch with no
functional changes. Check with:
$ git blame -s -CCC HEAD^..HEAD -- sequencer.c | grep -C3 '^[^^]'
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
---
builtin/revert.c | 821 +-----------------------------------------------------
sequencer.c | 802 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
sequencer.h | 26 ++
3 files changed, 828 insertions(+), 821 deletions(-)
diff --git a/builtin/revert.c b/builtin/revert.c
index df9459b..c272920 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -1,19 +1,9 @@
#include "cache.h"
#include "builtin.h"
-#include "object.h"
-#include "commit.h"
-#include "tag.h"
-#include "run-command.h"
-#include "exec_cmd.h"
-#include "utf8.h"
#include "parse-options.h"
-#include "cache-tree.h"
#include "diff.h"
#include "revision.h"
#include "rerere.h"
-#include "merge-recursive.h"
-#include "refs.h"
-#include "dir.h"
#include "sequencer.h"
/*
@@ -39,40 +29,11 @@ static const char * const cherry_pick_usage[] = {
NULL
};
-enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
-
-struct replay_opts {
- enum replay_action action;
- enum replay_subcommand subcommand;
-
- /* Boolean options */
- int edit;
- int record_origin;
- int no_commit;
- int signoff;
- int allow_ff;
- int allow_rerere_auto;
-
- int mainline;
-
- /* Merge strategy */
- const char *strategy;
- const char **xopts;
- size_t xopts_nr, xopts_alloc;
-
- /* Only used by REPLAY_NONE */
- struct rev_info *revs;
-};
-
-#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-
static const char *action_name(const struct replay_opts *opts)
{
return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
}
-static char *get_encoding(const char *message);
-
static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts)
{
return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage;
@@ -222,784 +183,6 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
usage_with_options(usage_str, options);
}
-struct commit_message {
- char *parent_label;
- const char *label;
- const char *subject;
- char *reencoded_message;
- const char *message;
-};
-
-static int get_message(struct commit *commit, struct commit_message *out)
-{
- const char *encoding;
- const char *abbrev, *subject;
- int abbrev_len, subject_len;
- char *q;
-
- if (!commit->buffer)
- return -1;
- encoding = get_encoding(commit->buffer);
- if (!encoding)
- encoding = "UTF-8";
- if (!git_commit_encoding)
- git_commit_encoding = "UTF-8";
-
- out->reencoded_message = NULL;
- out->message = commit->buffer;
- if (strcmp(encoding, git_commit_encoding))
- out->reencoded_message = reencode_string(commit->buffer,
- git_commit_encoding, encoding);
- if (out->reencoded_message)
- out->message = out->reencoded_message;
-
- abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
- abbrev_len = strlen(abbrev);
-
- subject_len = find_commit_subject(out->message, &subject);
-
- out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
- strlen("... ") + subject_len + 1);
- q = out->parent_label;
- q = mempcpy(q, "parent of ", strlen("parent of "));
- out->label = q;
- q = mempcpy(q, abbrev, abbrev_len);
- q = mempcpy(q, "... ", strlen("... "));
- out->subject = q;
- q = mempcpy(q, subject, subject_len);
- *q = '\0';
- return 0;
-}
-
-static void free_message(struct commit_message *msg)
-{
- free(msg->parent_label);
- free(msg->reencoded_message);
-}
-
-static char *get_encoding(const char *message)
-{
- const char *p = message, *eol;
-
- while (*p && *p != '\n') {
- for (eol = p + 1; *eol && *eol != '\n'; eol++)
- ; /* do nothing */
- if (!prefixcmp(p, "encoding ")) {
- char *result = xmalloc(eol - 8 - p);
- strlcpy(result, p + 9, eol - 8 - p);
- return result;
- }
- p = eol;
- if (*p == '\n')
- p++;
- }
- return NULL;
-}
-
-static void write_cherry_pick_head(struct commit *commit)
-{
- int fd;
- struct strbuf buf = STRBUF_INIT;
-
- strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
-
- fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
- if (fd < 0)
- die_errno(_("Could not open '%s' for writing"),
- git_path("CHERRY_PICK_HEAD"));
- if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
- die_errno(_("Could not write to '%s'"), git_path("CHERRY_PICK_HEAD"));
- strbuf_release(&buf);
-}
-
-static void print_advice(int show_hint)
-{
- char *msg = getenv("GIT_CHERRY_PICK_HELP");
-
- if (msg) {
- fprintf(stderr, "%s\n", msg);
- /*
- * A conflict has occured but the porcelain
- * (typically rebase --interactive) wants to take care
- * of the commit itself so remove CHERRY_PICK_HEAD
- */
- unlink(git_path("CHERRY_PICK_HEAD"));
- return;
- }
-
- if (show_hint) {
- advise("after resolving the conflicts, mark the corrected paths");
- advise("with 'git add <paths>' or 'git rm <paths>'");
- advise("and commit the result with 'git commit'");
- }
-}
-
-static void write_message(struct strbuf *msgbuf, const char *filename)
-{
- static struct lock_file msg_file;
-
- int msg_fd = hold_lock_file_for_update(&msg_file, filename,
- LOCK_DIE_ON_ERROR);
- if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
- die_errno(_("Could not write to %s."), filename);
- strbuf_release(msgbuf);
- if (commit_lock_file(&msg_file) < 0)
- die(_("Error wrapping up %s"), filename);
-}
-
-static struct tree *empty_tree(void)
-{
- return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
-}
-
-static int error_dirty_index(struct replay_opts *opts)
-{
- if (read_cache_unmerged())
- return error_resolve_conflict(action_name(opts));
-
- /* Different translation strings for cherry-pick and revert */
- if (opts->action == REPLAY_PICK)
- error(_("Your local changes would be overwritten by cherry-pick."));
- else
- error(_("Your local changes would be overwritten by revert."));
-
- if (advice_commit_before_merge)
- advise(_("Commit your changes or stash them to proceed."));
- return -1;
-}
-
-static int fast_forward_to(const unsigned char *to, const unsigned char *from)
-{
- struct ref_lock *ref_lock;
-
- read_cache();
- if (checkout_fast_forward(from, to))
- exit(1); /* the callee should have complained already */
- ref_lock = lock_any_ref_for_update("HEAD", from, 0);
- return write_ref_sha1(ref_lock, to, "cherry-pick");
-}
-
-static int do_recursive_merge(struct commit *base, struct commit *next,
- const char *base_label, const char *next_label,
- unsigned char *head, struct strbuf *msgbuf,
- struct replay_opts *opts)
-{
- struct merge_options o;
- struct tree *result, *next_tree, *base_tree, *head_tree;
- int clean, index_fd;
- const char **xopt;
- static struct lock_file index_lock;
-
- index_fd = hold_locked_index(&index_lock, 1);
-
- read_cache();
-
- init_merge_options(&o);
- o.ancestor = base ? base_label : "(empty tree)";
- o.branch1 = "HEAD";
- o.branch2 = next ? next_label : "(empty tree)";
-
- head_tree = parse_tree_indirect(head);
- next_tree = next ? next->tree : empty_tree();
- base_tree = base ? base->tree : empty_tree();
-
- for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
- parse_merge_opt(&o, *xopt);
-
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree, &result);
-
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock)))
- /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
- die(_("%s: Unable to write new index file"), action_name(opts));
- rollback_lock_file(&index_lock);
-
- if (!clean) {
- int i;
- strbuf_addstr(msgbuf, "\nConflicts:\n\n");
- for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- strbuf_addch(msgbuf, '\t');
- strbuf_addstr(msgbuf, ce->name);
- strbuf_addch(msgbuf, '\n');
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
- }
- }
-
- return !clean;
-}
-
-/*
- * If we are cherry-pick, and if the merge did not result in
- * hand-editing, we will hit this commit and inherit the original
- * author date and name.
- * If we are revert, or if our cherry-pick results in a hand merge,
- * we had better say that the current user is responsible for that.
- */
-static int run_git_commit(const char *defmsg, struct replay_opts *opts)
-{
- /* 6 is max possible length of our args array including NULL */
- const char *args[6];
- int i = 0;
-
- args[i++] = "commit";
- args[i++] = "-n";
- if (opts->signoff)
- args[i++] = "-s";
- if (!opts->edit) {
- args[i++] = "-F";
- args[i++] = defmsg;
- }
- args[i] = NULL;
-
- return run_command_v_opt(args, RUN_GIT_CMD);
-}
-
-static int do_pick_commit(struct commit *commit, enum replay_action action,
- struct replay_opts *opts)
-{
- unsigned char head[20];
- struct commit *base, *next, *parent;
- const char *base_label, *next_label;
- struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
- char *defmsg = NULL;
- struct strbuf msgbuf = STRBUF_INIT;
- int res;
-
- if (opts->no_commit) {
- /*
- * We do not intend to commit immediately. We just want to
- * merge the differences in, so let's compute the tree
- * that represents the "current" state for merge-recursive
- * to work on.
- */
- if (write_cache_as_tree(head, 0, NULL))
- die (_("Your index file is unmerged."));
- } else {
- if (get_sha1("HEAD", head))
- return error(_("You do not have a valid HEAD"));
- if (index_differs_from("HEAD", 0))
- return error_dirty_index(opts);
- }
- discard_cache();
-
- if (!commit->parents) {
- parent = NULL;
- }
- else if (commit->parents->next) {
- /* Reverting or cherry-picking a merge commit */
- int cnt;
- struct commit_list *p;
-
- if (!opts->mainline)
- return error(_("Commit %s is a merge but no -m option was given."),
- sha1_to_hex(commit->object.sha1));
-
- for (cnt = 1, p = commit->parents;
- cnt != opts->mainline && p;
- cnt++)
- p = p->next;
- if (cnt != opts->mainline || !p)
- return error(_("Commit %s does not have parent %d"),
- sha1_to_hex(commit->object.sha1), opts->mainline);
- parent = p->item;
- } else if (0 < opts->mainline)
- return error(_("Mainline was specified but commit %s is not a merge."),
- sha1_to_hex(commit->object.sha1));
- else
- parent = commit->parents->item;
-
- if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
- return fast_forward_to(commit->object.sha1, head);
-
- if (parent && parse_commit(parent) < 0)
- /* TRANSLATORS: The first %s will be "revert" or
- "cherry-pick", the second %s a SHA1 */
- return error(_("%s: cannot parse parent commit %s"),
- action_name(opts), sha1_to_hex(parent->object.sha1));
-
- if (get_message(commit, &msg) != 0)
- return error(_("Cannot get commit message for %s"),
- sha1_to_hex(commit->object.sha1));
-
- /*
- * "commit" is an existing commit. We would want to apply
- * the difference it introduces since its first parent "prev"
- * on top of the current HEAD if we are cherry-pick. Or the
- * reverse of it if we are revert.
- */
-
- defmsg = git_pathdup("MERGE_MSG");
-
- if (action == REPLAY_REVERT) {
- base = commit;
- base_label = msg.label;
- next = parent;
- next_label = msg.parent_label;
- strbuf_addstr(&msgbuf, "Revert \"");
- strbuf_addstr(&msgbuf, msg.subject);
- strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
- strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
-
- if (commit->parents && commit->parents->next) {
- strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
- strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
- }
- strbuf_addstr(&msgbuf, ".\n");
- } else {
- const char *p;
-
- base = parent;
- base_label = msg.parent_label;
- next = commit;
- next_label = msg.label;
-
- /*
- * Append the commit log message to msgbuf; it starts
- * after the tree, parent, author, committer
- * information followed by "\n\n".
- */
- p = strstr(msg.message, "\n\n");
- if (p) {
- p += 2;
- strbuf_addstr(&msgbuf, p);
- }
-
- if (opts->record_origin) {
- strbuf_addstr(&msgbuf, "(cherry picked from commit ");
- strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
- strbuf_addstr(&msgbuf, ")\n");
- }
- }
-
- if (!opts->strategy || !strcmp(opts->strategy, "recursive") || action == REPLAY_REVERT) {
- res = do_recursive_merge(base, next, base_label, next_label,
- head, &msgbuf, opts);
- write_message(&msgbuf, defmsg);
- } else {
- struct commit_list *common = NULL;
- struct commit_list *remotes = NULL;
-
- write_message(&msgbuf, defmsg);
-
- commit_list_insert(base, &common);
- commit_list_insert(next, &remotes);
- res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
- common, sha1_to_hex(head), remotes);
- free_commit_list(common);
- free_commit_list(remotes);
- }
-
- /*
- * If the merge was clean or if it failed due to conflict, we write
- * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
- * However, if the merge did not even start, then we don't want to
- * write it at all.
- */
- if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
- write_cherry_pick_head(commit);
-
- if (res) {
- error(action == REPLAY_REVERT
- ? _("could not revert %s... %s")
- : _("could not apply %s... %s"),
- find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
- msg.subject);
- print_advice(res == 1);
- rerere(opts->allow_rerere_auto);
- } else {
- if (!opts->no_commit)
- res = run_git_commit(defmsg, opts);
- }
-
- free_message(&msg);
- free(defmsg);
-
- return res;
-}
-
-static void prepare_revs(struct replay_opts *opts)
-{
- if (opts->action != REPLAY_REVERT)
- opts->revs->reverse ^= 1;
-
- if (prepare_revision_walk(opts->revs))
- die(_("revision walk setup failed"));
-
- if (!opts->revs->commits)
- die(_("empty commit set passed"));
-}
-
-static void read_and_refresh_cache(struct replay_opts *opts)
-{
- static struct lock_file index_lock;
- int index_fd = hold_locked_index(&index_lock, 0);
- if (read_index_preload(&the_index, NULL) < 0)
- die(_("git %s: failed to read the index"), action_name(opts));
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
- if (the_index.cache_changed) {
- if (write_index(&the_index, index_fd) ||
- commit_locked_index(&index_lock))
- die(_("git %s: failed to refresh the index"), action_name(opts));
- }
- rollback_lock_file(&index_lock);
-}
-
-/*
- * Append a commit to the end of the commit_list.
- *
- * next starts by pointing to the variable that holds the head of an
- * empty commit_list, and is updated to point to the "next" field of
- * the last item on the list as new commits are appended.
- *
- * Usage example:
- *
- * struct commit_list *list;
- * struct commit_list **next = &list;
- *
- * next = commit_list_append(c1, next);
- * next = commit_list_append(c2, next);
- * assert(commit_list_count(list) == 2);
- * return list;
- */
-static struct replay_insn_list **replay_insn_list_append(enum replay_action action,
- struct commit *operand,
- struct replay_insn_list **next)
-{
- struct replay_insn_list *new = xmalloc(sizeof(*new));
- new->action = action;
- new->operand = operand;
- *next = new;
- new->next = NULL;
- return &new->next;
-}
-
-static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)
-{
- struct replay_insn_list *cur;
-
- for (cur = todo_list; cur; cur = cur->next) {
- const char *sha1_abbrev, *action_str, *subject;
- int subject_len;
-
- action_str = cur->action == REPLAY_REVERT ? "revert" : "pick";
- sha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);
- subject_len = find_commit_subject(cur->operand->buffer, &subject);
- strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
- subject_len, subject);
- }
- return 0;
-}
-
-static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)
-{
- unsigned char commit_sha1[20];
- char *end_of_object_name;
- int saved, status;
-
- if (!prefixcmp(bol, "pick ")) {
- item->action = REPLAY_PICK;
- bol += strlen("pick ");
- } else if (!prefixcmp(bol, "revert ")) {
- item->action = REPLAY_REVERT;
- bol += strlen("revert ");
- } else {
- size_t len = strchrnul(bol, '\n') - bol;
- if (len > 255)
- len = 255;
- return error(_("Unrecognized action: %.*s"), (int)len, bol);
- }
-
- end_of_object_name = bol + strcspn(bol, " \n");
- saved = *end_of_object_name;
- *end_of_object_name = '\0';
- status = get_sha1(bol, commit_sha1);
- *end_of_object_name = saved;
-
- if (status < 0)
- return error(_("Malformed object name: %s"), bol);
-
- item->operand = lookup_commit_reference(commit_sha1);
- if (!item->operand)
- return error(_("Not a valid commit: %s"), bol);
-
- item->next = NULL;
- return 0;
-}
-
-static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)
-{
- struct replay_insn_list **next = todo_list;
- struct replay_insn_list item = {0, NULL, NULL};
- char *p = buf;
- int i;
-
- for (i = 1; *p; i++) {
- char *eol = strchrnul(p, '\n');
- if (parse_insn_line(p, eol, &item) < 0)
- return error(_("on line %d."), i);
- next = replay_insn_list_append(item.action, item.operand, next);
- p = *eol ? eol + 1 : eol;
- }
- if (!*todo_list)
- return error(_("No commits parsed."));
- return 0;
-}
-
-static void read_populate_todo(struct replay_insn_list **todo_list)
-{
- const char *todo_file = git_path(SEQ_TODO_FILE);
- struct strbuf buf = STRBUF_INIT;
- int fd, res;
-
- fd = open(todo_file, O_RDONLY);
- if (fd < 0)
- die_errno(_("Could not open %s."), todo_file);
- if (strbuf_read(&buf, fd, 0) < 0) {
- close(fd);
- strbuf_release(&buf);
- die(_("Could not read %s."), todo_file);
- }
- close(fd);
-
- res = parse_insn_buffer(buf.buf, todo_list);
- strbuf_release(&buf);
- if (res)
- die(_("Unusable instruction sheet: %s"), todo_file);
-}
-
-static int populate_opts_cb(const char *key, const char *value, void *data)
-{
- struct replay_opts *opts = data;
- int error_flag = 1;
-
- if (!value)
- error_flag = 0;
- else if (!strcmp(key, "options.no-commit"))
- opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.edit"))
- opts->edit = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.signoff"))
- opts->signoff = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.record-origin"))
- opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.allow-ff"))
- opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.mainline"))
- opts->mainline = git_config_int(key, value);
- else if (!strcmp(key, "options.strategy"))
- git_config_string(&opts->strategy, key, value);
- else if (!strcmp(key, "options.strategy-option")) {
- ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
- opts->xopts[opts->xopts_nr++] = xstrdup(value);
- } else
- return error(_("Invalid key: %s"), key);
-
- if (!error_flag)
- return error(_("Invalid value for %s: %s"), key, value);
-
- return 0;
-}
-
-static void read_populate_opts(struct replay_opts **opts_ptr)
-{
- const char *opts_file = git_path(SEQ_OPTS_FILE);
-
- if (!file_exists(opts_file))
- return;
- if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
- die(_("Malformed options sheet: %s"), opts_file);
-}
-
-static void walk_revs_populate_todo(struct replay_insn_list **todo_list,
- struct replay_opts *opts)
-{
- struct commit *commit;
- struct replay_insn_list **next;
-
- prepare_revs(opts);
-
- next = todo_list;
- while ((commit = get_revision(opts->revs)))
- next = replay_insn_list_append(opts->action, commit, next);
-}
-
-static int create_seq_dir(void)
-{
- const char *seq_dir = git_path(SEQ_DIR);
-
- if (file_exists(seq_dir))
- return error(_("%s already exists."), seq_dir);
- else if (mkdir(seq_dir, 0777) < 0)
- die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
- return 0;
-}
-
-static void save_head(const char *head)
-{
- const char *head_file = git_path(SEQ_HEAD_FILE);
- static struct lock_file head_lock;
- struct strbuf buf = STRBUF_INIT;
- int fd;
-
- fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
- strbuf_addf(&buf, "%s\n", head);
- if (write_in_full(fd, buf.buf, buf.len) < 0)
- die_errno(_("Could not write to %s."), head_file);
- if (commit_lock_file(&head_lock) < 0)
- die(_("Error wrapping up %s."), head_file);
-}
-
-static void save_todo(struct replay_insn_list *todo_list)
-{
- const char *todo_file = git_path(SEQ_TODO_FILE);
- static struct lock_file todo_lock;
- struct strbuf buf = STRBUF_INIT;
- int fd;
-
- fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
- if (format_todo(&buf, todo_list) < 0)
- die(_("Could not format %s."), todo_file);
- if (write_in_full(fd, buf.buf, buf.len) < 0) {
- strbuf_release(&buf);
- die_errno(_("Could not write to %s."), todo_file);
- }
- if (commit_lock_file(&todo_lock) < 0) {
- strbuf_release(&buf);
- die(_("Error wrapping up %s."), todo_file);
- }
- strbuf_release(&buf);
-}
-
-static void save_opts(struct replay_opts *opts)
-{
- const char *opts_file = git_path(SEQ_OPTS_FILE);
-
- if (opts->no_commit)
- git_config_set_in_file(opts_file, "options.no-commit", "true");
- if (opts->edit)
- git_config_set_in_file(opts_file, "options.edit", "true");
- if (opts->signoff)
- git_config_set_in_file(opts_file, "options.signoff", "true");
- if (opts->record_origin)
- git_config_set_in_file(opts_file, "options.record-origin", "true");
- if (opts->allow_ff)
- git_config_set_in_file(opts_file, "options.allow-ff", "true");
- if (opts->mainline) {
- struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%d", opts->mainline);
- git_config_set_in_file(opts_file, "options.mainline", buf.buf);
- strbuf_release(&buf);
- }
- if (opts->strategy)
- git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
- if (opts->xopts) {
- int i;
- for (i = 0; i < opts->xopts_nr; i++)
- git_config_set_multivar_in_file(opts_file,
- "options.strategy-option",
- opts->xopts[i], "^$", 0);
- }
-}
-
-static int pick_commits(struct replay_insn_list *todo_list,
- struct replay_opts *opts)
-{
- struct replay_insn_list *cur;
- int res;
-
- setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
- if (opts->allow_ff)
- assert(!(opts->signoff || opts->no_commit ||
- opts->record_origin || opts->edit));
- read_and_refresh_cache(opts);
-
- for (cur = todo_list; cur; cur = cur->next) {
- save_todo(cur);
- res = do_pick_commit(cur->operand, cur->action, opts);
- if (res) {
- if (!cur->next)
- /*
- * An error was encountered while
- * picking the last commit; the
- * sequencer state is useless now --
- * the user simply needs to resolve
- * the conflict and commit
- */
- remove_sequencer_state(0);
- return res;
- }
- }
-
- /*
- * Sequence of picks finished successfully; cleanup by
- * removing the .git/sequencer directory
- */
- remove_sequencer_state(1);
- return 0;
-}
-
-static int pick_revisions(struct replay_opts *opts)
-{
- struct replay_insn_list *todo_list = NULL;
- unsigned char sha1[20];
-
- if (opts->subcommand == REPLAY_NONE)
- assert(opts->revs);
-
- read_and_refresh_cache(opts);
-
- /*
- * Decide what to do depending on the arguments; a fresh
- * cherry-pick should be handled differently from an existing
- * one that is being continued
- */
- if (opts->subcommand == REPLAY_RESET) {
- remove_sequencer_state(1);
- return 0;
- } else if (opts->subcommand == REPLAY_CONTINUE) {
- if (!file_exists(git_path(SEQ_TODO_FILE)))
- goto error;
- read_populate_opts(&opts);
- read_populate_todo(&todo_list);
-
- /* Verify that the conflict has been resolved */
- if (!index_differs_from("HEAD", 0))
- todo_list = todo_list->next;
- } else {
- /*
- * Start a new cherry-pick/ revert sequence; but
- * first, make sure that an existing one isn't in
- * progress
- */
-
- walk_revs_populate_todo(&todo_list, opts);
- if (create_seq_dir() < 0) {
- error(_("A cherry-pick or revert is in progress."));
- advise(_("Use --continue to continue the operation"));
- advise(_("or --reset to forget about it"));
- return -1;
- }
- if (get_sha1("HEAD", sha1)) {
- if (opts->action == REPLAY_REVERT)
- return error(_("Can't revert as initial commit"));
- return error(_("Can't cherry-pick into empty head"));
- }
- save_head(sha1_to_hex(sha1));
- save_opts(opts);
- }
- return pick_commits(todo_list, opts);
-error:
- return error(_("No %s in progress"), action_name(opts));
-}
-
int cmd_revert(int argc, const char **argv, const char *prefix)
{
struct replay_opts opts;
@@ -1011,7 +194,7 @@ int cmd_revert(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_REVERT;
git_config(git_default_config, NULL);
parse_args(argc, argv, &opts);
- res = pick_revisions(&opts);
+ res = sequencer_pick_revisions(&opts);
if (res < 0)
die(_("revert failed"));
return res;
@@ -1026,7 +209,7 @@ int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
opts.action = REPLAY_PICK;
git_config(git_default_config, NULL);
parse_args(argc, argv, &opts);
- res = pick_revisions(&opts);
+ res = sequencer_pick_revisions(&opts);
if (res < 0)
die(_("cherry-pick failed"));
return res;
diff --git a/sequencer.c b/sequencer.c
index bc2c046..87f146b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,7 +1,27 @@
#include "cache.h"
-#include "sequencer.h"
-#include "strbuf.h"
+#include "object.h"
+#include "commit.h"
+#include "tag.h"
+#include "run-command.h"
+#include "exec_cmd.h"
+#include "utf8.h"
+#include "cache-tree.h"
+#include "diff.h"
+#include "revision.h"
+#include "rerere.h"
+#include "merge-recursive.h"
+#include "refs.h"
#include "dir.h"
+#include "sequencer.h"
+
+#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
+
+static const char *action_name(const struct replay_opts *opts)
+{
+ return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
+}
+
+static char *get_encoding(const char *message);
void remove_sequencer_state(int aggressive)
{
@@ -17,3 +37,781 @@ void remove_sequencer_state(int aggressive)
strbuf_release(&seq_dir);
strbuf_release(&seq_old_dir);
}
+
+struct commit_message {
+ char *parent_label;
+ const char *label;
+ const char *subject;
+ char *reencoded_message;
+ const char *message;
+};
+
+static int get_message(struct commit *commit, struct commit_message *out)
+{
+ const char *encoding;
+ const char *abbrev, *subject;
+ int abbrev_len, subject_len;
+ char *q;
+
+ if (!commit->buffer)
+ return -1;
+ encoding = get_encoding(commit->buffer);
+ if (!encoding)
+ encoding = "UTF-8";
+ if (!git_commit_encoding)
+ git_commit_encoding = "UTF-8";
+
+ out->reencoded_message = NULL;
+ out->message = commit->buffer;
+ if (strcmp(encoding, git_commit_encoding))
+ out->reencoded_message = reencode_string(commit->buffer,
+ git_commit_encoding, encoding);
+ if (out->reencoded_message)
+ out->message = out->reencoded_message;
+
+ abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+ abbrev_len = strlen(abbrev);
+
+ subject_len = find_commit_subject(out->message, &subject);
+
+ out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
+ strlen("... ") + subject_len + 1);
+ q = out->parent_label;
+ q = mempcpy(q, "parent of ", strlen("parent of "));
+ out->label = q;
+ q = mempcpy(q, abbrev, abbrev_len);
+ q = mempcpy(q, "... ", strlen("... "));
+ out->subject = q;
+ q = mempcpy(q, subject, subject_len);
+ *q = '\0';
+ return 0;
+}
+
+static void free_message(struct commit_message *msg)
+{
+ free(msg->parent_label);
+ free(msg->reencoded_message);
+}
+
+static char *get_encoding(const char *message)
+{
+ const char *p = message, *eol;
+
+ while (*p && *p != '\n') {
+ for (eol = p + 1; *eol && *eol != '\n'; eol++)
+ ; /* do nothing */
+ if (!prefixcmp(p, "encoding ")) {
+ char *result = xmalloc(eol - 8 - p);
+ strlcpy(result, p + 9, eol - 8 - p);
+ return result;
+ }
+ p = eol;
+ if (*p == '\n')
+ p++;
+ }
+ return NULL;
+}
+
+static void write_cherry_pick_head(struct commit *commit)
+{
+ int fd;
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
+
+ fd = open(git_path("CHERRY_PICK_HEAD"), O_WRONLY | O_CREAT, 0666);
+ if (fd < 0)
+ die_errno(_("Could not open '%s' for writing"),
+ git_path("CHERRY_PICK_HEAD"));
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len || close(fd))
+ die_errno(_("Could not write to '%s'"), git_path("CHERRY_PICK_HEAD"));
+ strbuf_release(&buf);
+}
+
+static void print_advice(int show_hint)
+{
+ char *msg = getenv("GIT_CHERRY_PICK_HELP");
+
+ if (msg) {
+ fprintf(stderr, "%s\n", msg);
+ /*
+ * A conflict has occured but the porcelain
+ * (typically rebase --interactive) wants to take care
+ * of the commit itself so remove CHERRY_PICK_HEAD
+ */
+ unlink(git_path("CHERRY_PICK_HEAD"));
+ return;
+ }
+
+ if (show_hint) {
+ advise("after resolving the conflicts, mark the corrected paths");
+ advise("with 'git add <paths>' or 'git rm <paths>'");
+ advise("and commit the result with 'git commit'");
+ }
+}
+
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+ static struct lock_file msg_file;
+
+ int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+ LOCK_DIE_ON_ERROR);
+ if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+ die_errno(_("Could not write to %s."), filename);
+ strbuf_release(msgbuf);
+ if (commit_lock_file(&msg_file) < 0)
+ die(_("Error wrapping up %s"), filename);
+}
+
+static struct tree *empty_tree(void)
+{
+ return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
+}
+
+static int error_dirty_index(struct replay_opts *opts)
+{
+ if (read_cache_unmerged())
+ return error_resolve_conflict(action_name(opts));
+
+ /* Different translation strings for cherry-pick and revert */
+ if (opts->action == REPLAY_PICK)
+ error(_("Your local changes would be overwritten by cherry-pick."));
+ else
+ error(_("Your local changes would be overwritten by revert."));
+
+ if (advice_commit_before_merge)
+ advise(_("Commit your changes or stash them to proceed."));
+ return -1;
+}
+
+static int fast_forward_to(const unsigned char *to, const unsigned char *from)
+{
+ struct ref_lock *ref_lock;
+
+ read_cache();
+ if (checkout_fast_forward(from, to))
+ exit(1); /* the callee should have complained already */
+ ref_lock = lock_any_ref_for_update("HEAD", from, 0);
+ return write_ref_sha1(ref_lock, to, "cherry-pick");
+}
+
+static int do_recursive_merge(struct commit *base, struct commit *next,
+ const char *base_label, const char *next_label,
+ unsigned char *head, struct strbuf *msgbuf,
+ struct replay_opts *opts)
+{
+ struct merge_options o;
+ struct tree *result, *next_tree, *base_tree, *head_tree;
+ int clean, index_fd;
+ const char **xopt;
+ static struct lock_file index_lock;
+
+ index_fd = hold_locked_index(&index_lock, 1);
+
+ read_cache();
+
+ init_merge_options(&o);
+ o.ancestor = base ? base_label : "(empty tree)";
+ o.branch1 = "HEAD";
+ o.branch2 = next ? next_label : "(empty tree)";
+
+ head_tree = parse_tree_indirect(head);
+ next_tree = next ? next->tree : empty_tree();
+ base_tree = base ? base->tree : empty_tree();
+
+ for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++)
+ parse_merge_opt(&o, *xopt);
+
+ clean = merge_trees(&o,
+ head_tree,
+ next_tree, base_tree, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock)))
+ /* TRANSLATORS: %s will be "revert" or "cherry-pick" */
+ die(_("%s: Unable to write new index file"), action_name(opts));
+ rollback_lock_file(&index_lock);
+
+ if (!clean) {
+ int i;
+ strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+ for (i = 0; i < active_nr;) {
+ struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_addch(msgbuf, '\t');
+ strbuf_addstr(msgbuf, ce->name);
+ strbuf_addch(msgbuf, '\n');
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
+ }
+
+ return !clean;
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg, struct replay_opts *opts)
+{
+ /* 6 is max possible length of our args array including NULL */
+ const char *args[6];
+ int i = 0;
+
+ args[i++] = "commit";
+ args[i++] = "-n";
+ if (opts->signoff)
+ args[i++] = "-s";
+ if (!opts->edit) {
+ args[i++] = "-F";
+ args[i++] = defmsg;
+ }
+ args[i] = NULL;
+
+ return run_command_v_opt(args, RUN_GIT_CMD);
+}
+
+static int do_pick_commit(struct commit *commit, enum replay_action action,
+ struct replay_opts *opts)
+{
+ unsigned char head[20];
+ struct commit *base, *next, *parent;
+ const char *base_label, *next_label;
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
+ char *defmsg = NULL;
+ struct strbuf msgbuf = STRBUF_INIT;
+ int res;
+
+ if (opts->no_commit) {
+ /*
+ * We do not intend to commit immediately. We just want to
+ * merge the differences in, so let's compute the tree
+ * that represents the "current" state for merge-recursive
+ * to work on.
+ */
+ if (write_cache_as_tree(head, 0, NULL))
+ die (_("Your index file is unmerged."));
+ } else {
+ if (get_sha1("HEAD", head))
+ return error(_("You do not have a valid HEAD"));
+ if (index_differs_from("HEAD", 0))
+ return error_dirty_index(opts);
+ }
+ discard_cache();
+
+ if (!commit->parents) {
+ parent = NULL;
+ }
+ else if (commit->parents->next) {
+ /* Reverting or cherry-picking a merge commit */
+ int cnt;
+ struct commit_list *p;
+
+ if (!opts->mainline)
+ return error(_("Commit %s is a merge but no -m option was given."),
+ sha1_to_hex(commit->object.sha1));
+
+ for (cnt = 1, p = commit->parents;
+ cnt != opts->mainline && p;
+ cnt++)
+ p = p->next;
+ if (cnt != opts->mainline || !p)
+ return error(_("Commit %s does not have parent %d"),
+ sha1_to_hex(commit->object.sha1), opts->mainline);
+ parent = p->item;
+ } else if (0 < opts->mainline)
+ return error(_("Mainline was specified but commit %s is not a merge."),
+ sha1_to_hex(commit->object.sha1));
+ else
+ parent = commit->parents->item;
+
+ if (opts->allow_ff && parent && !hashcmp(parent->object.sha1, head))
+ return fast_forward_to(commit->object.sha1, head);
+
+ if (parent && parse_commit(parent) < 0)
+ /* TRANSLATORS: The first %s will be "revert" or
+ "cherry-pick", the second %s a SHA1 */
+ return error(_("%s: cannot parse parent commit %s"),
+ action_name(opts), sha1_to_hex(parent->object.sha1));
+
+ if (get_message(commit, &msg) != 0)
+ return error(_("Cannot get commit message for %s"),
+ sha1_to_hex(commit->object.sha1));
+
+ /*
+ * "commit" is an existing commit. We would want to apply
+ * the difference it introduces since its first parent "prev"
+ * on top of the current HEAD if we are cherry-pick. Or the
+ * reverse of it if we are revert.
+ */
+
+ defmsg = git_pathdup("MERGE_MSG");
+
+ if (action == REPLAY_REVERT) {
+ base = commit;
+ base_label = msg.label;
+ next = parent;
+ next_label = msg.parent_label;
+ strbuf_addstr(&msgbuf, "Revert \"");
+ strbuf_addstr(&msgbuf, msg.subject);
+ strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+
+ if (commit->parents && commit->parents->next) {
+ strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
+ }
+ strbuf_addstr(&msgbuf, ".\n");
+ } else {
+ const char *p;
+
+ base = parent;
+ base_label = msg.parent_label;
+ next = commit;
+ next_label = msg.label;
+
+ /*
+ * Append the commit log message to msgbuf; it starts
+ * after the tree, parent, author, committer
+ * information followed by "\n\n".
+ */
+ p = strstr(msg.message, "\n\n");
+ if (p) {
+ p += 2;
+ strbuf_addstr(&msgbuf, p);
+ }
+
+ if (opts->record_origin) {
+ strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, ")\n");
+ }
+ }
+
+ if (!opts->strategy || !strcmp(opts->strategy, "recursive") || action == REPLAY_REVERT) {
+ res = do_recursive_merge(base, next, base_label, next_label,
+ head, &msgbuf, opts);
+ write_message(&msgbuf, defmsg);
+ } else {
+ struct commit_list *common = NULL;
+ struct commit_list *remotes = NULL;
+
+ write_message(&msgbuf, defmsg);
+
+ commit_list_insert(base, &common);
+ commit_list_insert(next, &remotes);
+ res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,
+ common, sha1_to_hex(head), remotes);
+ free_commit_list(common);
+ free_commit_list(remotes);
+ }
+
+ /*
+ * If the merge was clean or if it failed due to conflict, we write
+ * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
+ * However, if the merge did not even start, then we don't want to
+ * write it at all.
+ */
+ if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
+ write_cherry_pick_head(commit);
+
+ if (res) {
+ error(action == REPLAY_REVERT
+ ? _("could not revert %s... %s")
+ : _("could not apply %s... %s"),
+ find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+ msg.subject);
+ print_advice(res == 1);
+ rerere(opts->allow_rerere_auto);
+ } else {
+ if (!opts->no_commit)
+ res = run_git_commit(defmsg, opts);
+ }
+
+ free_message(&msg);
+ free(defmsg);
+
+ return res;
+}
+
+static void prepare_revs(struct replay_opts *opts)
+{
+ if (opts->action != REPLAY_REVERT)
+ opts->revs->reverse ^= 1;
+
+ if (prepare_revision_walk(opts->revs))
+ die(_("revision walk setup failed"));
+
+ if (!opts->revs->commits)
+ die(_("empty commit set passed"));
+}
+
+static void read_and_refresh_cache(struct replay_opts *opts)
+{
+ static struct lock_file index_lock;
+ int index_fd = hold_locked_index(&index_lock, 0);
+ if (read_index_preload(&the_index, NULL) < 0)
+ die(_("git %s: failed to read the index"), action_name(opts));
+ refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
+ if (the_index.cache_changed) {
+ if (write_index(&the_index, index_fd) ||
+ commit_locked_index(&index_lock))
+ die(_("git %s: failed to refresh the index"), action_name(opts));
+ }
+ rollback_lock_file(&index_lock);
+}
+
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ * struct commit_list *list;
+ * struct commit_list **next = &list;
+ *
+ * next = commit_list_append(c1, next);
+ * next = commit_list_append(c2, next);
+ * assert(commit_list_count(list) == 2);
+ * return list;
+ */
+static struct replay_insn_list **replay_insn_list_append(enum replay_action action,
+ struct commit *operand,
+ struct replay_insn_list **next)
+{
+ struct replay_insn_list *new = xmalloc(sizeof(*new));
+ new->action = action;
+ new->operand = operand;
+ *next = new;
+ new->next = NULL;
+ return &new->next;
+}
+
+static int format_todo(struct strbuf *buf, struct replay_insn_list *todo_list)
+{
+ struct replay_insn_list *cur;
+
+ for (cur = todo_list; cur; cur = cur->next) {
+ const char *sha1_abbrev, *action_str, *subject;
+ int subject_len;
+
+ action_str = cur->action == REPLAY_REVERT ? "revert" : "pick";
+ sha1_abbrev = find_unique_abbrev(cur->operand->object.sha1, DEFAULT_ABBREV);
+ subject_len = find_commit_subject(cur->operand->buffer, &subject);
+ strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
+ subject_len, subject);
+ }
+ return 0;
+}
+
+static int parse_insn_line(char *bol, char *eol, struct replay_insn_list *item)
+{
+ unsigned char commit_sha1[20];
+ char *end_of_object_name;
+ int saved, status;
+
+ if (!prefixcmp(bol, "pick ")) {
+ item->action = REPLAY_PICK;
+ bol += strlen("pick ");
+ } else if (!prefixcmp(bol, "revert ")) {
+ item->action = REPLAY_REVERT;
+ bol += strlen("revert ");
+ } else {
+ size_t len = strchrnul(bol, '\n') - bol;
+ if (len > 255)
+ len = 255;
+ return error(_("Unrecognized action: %.*s"), (int)len, bol);
+ }
+
+ end_of_object_name = bol + strcspn(bol, " \n");
+ saved = *end_of_object_name;
+ *end_of_object_name = '\0';
+ status = get_sha1(bol, commit_sha1);
+ *end_of_object_name = saved;
+
+ if (status < 0)
+ return error(_("Malformed object name: %s"), bol);
+
+ item->operand = lookup_commit_reference(commit_sha1);
+ if (!item->operand)
+ return error(_("Not a valid commit: %s"), bol);
+
+ item->next = NULL;
+ return 0;
+}
+
+static int parse_insn_buffer(char *buf, struct replay_insn_list **todo_list)
+{
+ struct replay_insn_list **next = todo_list;
+ struct replay_insn_list item = {0, NULL, NULL};
+ char *p = buf;
+ int i;
+
+ for (i = 1; *p; i++) {
+ char *eol = strchrnul(p, '\n');
+ if (parse_insn_line(p, eol, &item) < 0)
+ return error(_("on line %d."), i);
+ next = replay_insn_list_append(item.action, item.operand, next);
+ p = *eol ? eol + 1 : eol;
+ }
+ if (!*todo_list)
+ return error(_("No commits parsed."));
+ return 0;
+}
+
+static void read_populate_todo(struct replay_insn_list **todo_list)
+{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
+ struct strbuf buf = STRBUF_INIT;
+ int fd, res;
+
+ fd = open(todo_file, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("Could not open %s."), todo_file);
+ if (strbuf_read(&buf, fd, 0) < 0) {
+ close(fd);
+ strbuf_release(&buf);
+ die(_("Could not read %s."), todo_file);
+ }
+ close(fd);
+
+ res = parse_insn_buffer(buf.buf, todo_list);
+ strbuf_release(&buf);
+ if (res)
+ die(_("Unusable instruction sheet: %s"), todo_file);
+}
+
+static int populate_opts_cb(const char *key, const char *value, void *data)
+{
+ struct replay_opts *opts = data;
+ int error_flag = 1;
+
+ if (!value)
+ error_flag = 0;
+ else if (!strcmp(key, "options.no-commit"))
+ opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.edit"))
+ opts->edit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.signoff"))
+ opts->signoff = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.record-origin"))
+ opts->record_origin = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.allow-ff"))
+ opts->allow_ff = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.mainline"))
+ opts->mainline = git_config_int(key, value);
+ else if (!strcmp(key, "options.strategy"))
+ git_config_string(&opts->strategy, key, value);
+ else if (!strcmp(key, "options.strategy-option")) {
+ ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
+ opts->xopts[opts->xopts_nr++] = xstrdup(value);
+ } else
+ return error(_("Invalid key: %s"), key);
+
+ if (!error_flag)
+ return error(_("Invalid value for %s: %s"), key, value);
+
+ return 0;
+}
+
+static void read_populate_opts(struct replay_opts **opts_ptr)
+{
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+ if (!file_exists(opts_file))
+ return;
+ if (git_config_from_file(populate_opts_cb, opts_file, *opts_ptr) < 0)
+ die(_("Malformed options sheet: %s"), opts_file);
+}
+
+static void walk_revs_populate_todo(struct replay_insn_list **todo_list,
+ struct replay_opts *opts)
+{
+ struct commit *commit;
+ struct replay_insn_list **next;
+
+ prepare_revs(opts);
+
+ next = todo_list;
+ while ((commit = get_revision(opts->revs)))
+ next = replay_insn_list_append(opts->action, commit, next);
+}
+
+static int create_seq_dir(void)
+{
+ const char *seq_dir = git_path(SEQ_DIR);
+
+ if (file_exists(seq_dir))
+ return error(_("%s already exists."), seq_dir);
+ else if (mkdir(seq_dir, 0777) < 0)
+ die_errno(_("Could not create sequencer directory '%s'."), seq_dir);
+ return 0;
+}
+
+static void save_head(const char *head)
+{
+ const char *head_file = git_path(SEQ_HEAD_FILE);
+ static struct lock_file head_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
+
+ fd = hold_lock_file_for_update(&head_lock, head_file, LOCK_DIE_ON_ERROR);
+ strbuf_addf(&buf, "%s\n", head);
+ if (write_in_full(fd, buf.buf, buf.len) < 0)
+ die_errno(_("Could not write to %s."), head_file);
+ if (commit_lock_file(&head_lock) < 0)
+ die(_("Error wrapping up %s."), head_file);
+}
+
+static void save_todo(struct replay_insn_list *todo_list)
+{
+ const char *todo_file = git_path(SEQ_TODO_FILE);
+ static struct lock_file todo_lock;
+ struct strbuf buf = STRBUF_INIT;
+ int fd;
+
+ fd = hold_lock_file_for_update(&todo_lock, todo_file, LOCK_DIE_ON_ERROR);
+ if (format_todo(&buf, todo_list) < 0)
+ die(_("Could not format %s."), todo_file);
+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
+ strbuf_release(&buf);
+ die_errno(_("Could not write to %s."), todo_file);
+ }
+ if (commit_lock_file(&todo_lock) < 0) {
+ strbuf_release(&buf);
+ die(_("Error wrapping up %s."), todo_file);
+ }
+ strbuf_release(&buf);
+}
+
+static void save_opts(struct replay_opts *opts)
+{
+ const char *opts_file = git_path(SEQ_OPTS_FILE);
+
+ if (opts->no_commit)
+ git_config_set_in_file(opts_file, "options.no-commit", "true");
+ if (opts->edit)
+ git_config_set_in_file(opts_file, "options.edit", "true");
+ if (opts->signoff)
+ git_config_set_in_file(opts_file, "options.signoff", "true");
+ if (opts->record_origin)
+ git_config_set_in_file(opts_file, "options.record-origin", "true");
+ if (opts->allow_ff)
+ git_config_set_in_file(opts_file, "options.allow-ff", "true");
+ if (opts->mainline) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "%d", opts->mainline);
+ git_config_set_in_file(opts_file, "options.mainline", buf.buf);
+ strbuf_release(&buf);
+ }
+ if (opts->strategy)
+ git_config_set_in_file(opts_file, "options.strategy", opts->strategy);
+ if (opts->xopts) {
+ int i;
+ for (i = 0; i < opts->xopts_nr; i++)
+ git_config_set_multivar_in_file(opts_file,
+ "options.strategy-option",
+ opts->xopts[i], "^$", 0);
+ }
+}
+
+static int pick_commits(struct replay_insn_list *todo_list,
+ struct replay_opts *opts)
+{
+ struct replay_insn_list *cur;
+ int res;
+
+ setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
+ if (opts->allow_ff)
+ assert(!(opts->signoff || opts->no_commit ||
+ opts->record_origin || opts->edit));
+ read_and_refresh_cache(opts);
+
+ for (cur = todo_list; cur; cur = cur->next) {
+ save_todo(cur);
+ res = do_pick_commit(cur->operand, cur->action, opts);
+ if (res) {
+ if (!cur->next)
+ /*
+ * An error was encountered while
+ * picking the last commit; the
+ * sequencer state is useless now --
+ * the user simply needs to resolve
+ * the conflict and commit
+ */
+ remove_sequencer_state(0);
+ return res;
+ }
+ }
+
+ /*
+ * Sequence of picks finished successfully; cleanup by
+ * removing the .git/sequencer directory
+ */
+ remove_sequencer_state(1);
+ return 0;
+}
+
+int sequencer_pick_revisions(struct replay_opts *opts)
+{
+ struct replay_insn_list *todo_list = NULL;
+ unsigned char sha1[20];
+
+ if (opts->subcommand == REPLAY_NONE)
+ assert(opts->revs);
+
+ read_and_refresh_cache(opts);
+
+ /*
+ * Decide what to do depending on the arguments; a fresh
+ * cherry-pick should be handled differently from an existing
+ * one that is being continued
+ */
+ if (opts->subcommand == REPLAY_RESET) {
+ remove_sequencer_state(1);
+ return 0;
+ } else if (opts->subcommand == REPLAY_CONTINUE) {
+ if (!file_exists(git_path(SEQ_TODO_FILE)))
+ goto error;
+ read_populate_opts(&opts);
+ read_populate_todo(&todo_list);
+
+ /* Verify that the conflict has been resolved */
+ if (!index_differs_from("HEAD", 0))
+ todo_list = todo_list->next;
+ } else {
+ /*
+ * Start a new cherry-pick/ revert sequence; but
+ * first, make sure that an existing one isn't in
+ * progress
+ */
+
+ walk_revs_populate_todo(&todo_list, opts);
+ if (create_seq_dir() < 0) {
+ error(_("A cherry-pick or revert is in progress."));
+ advise(_("Use --continue to continue the operation"));
+ advise(_("or --reset to forget about it"));
+ return -1;
+ }
+ if (get_sha1("HEAD", sha1)) {
+ if (opts->action == REPLAY_REVERT)
+ return error(_("Can't revert as initial commit"));
+ return error(_("Can't cherry-pick into empty head"));
+ }
+ save_head(sha1_to_hex(sha1));
+ save_opts(opts);
+ }
+ return pick_commits(todo_list, opts);
+error:
+ return error(_("No %s in progress"), action_name(opts));
+}
diff --git a/sequencer.h b/sequencer.h
index f4db257..92b2d63 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -8,6 +8,30 @@
#define SEQ_OPTS_FILE "sequencer/opts"
enum replay_action { REPLAY_REVERT, REPLAY_PICK };
+enum replay_subcommand { REPLAY_NONE, REPLAY_RESET, REPLAY_CONTINUE };
+
+struct replay_opts {
+ enum replay_action action;
+ enum replay_subcommand subcommand;
+
+ /* Boolean options */
+ int edit;
+ int record_origin;
+ int no_commit;
+ int signoff;
+ int allow_ff;
+ int allow_rerere_auto;
+
+ int mainline;
+
+ /* Merge strategy */
+ const char *strategy;
+ const char **xopts;
+ size_t xopts_nr, xopts_alloc;
+
+ /* Only used by REPLAY_NONE */
+ struct rev_info *revs;
+};
struct replay_insn_list {
enum replay_action action;
@@ -25,4 +49,6 @@ struct replay_insn_list {
*/
void remove_sequencer_state(int aggressive);
+int sequencer_pick_revisions(struct replay_opts *opts);
+
#endif
--
1.7.6.351.gb35ac.dirty
^ permalink raw reply related
* [PATCH 0/5] Sequencer: working around historical mistakes
From: Ramkumar Ramachandra @ 2011-11-05 16:29 UTC (permalink / raw)
To: Git List; +Cc: Junio C Hamano, Jonathan Nieder, Christian Couder
Hi,
As described in the discussion following $gmane/179304/focus=179383,
we have decided to handle historical hacks in the sequencer itself.
This series that follows is one step in the right direction.
- Part 1/5 makes the gigantic move required to create the sequencer.
If you need an excuse to celebrate, wait till this gets merged :)
- Part 5/5 can be considered as the "ultimate objective" of the
series. I first wrote this part, and then wrote the other parts to
make tests pass.
- Parts 3/5 and 4/5 are ugly! Causes heartburn.
Immediate shortcomings of this iteration:
1. No tests yet. I want to see if it's possible to make this less
ugly first.
2. This series depends on rr/revert-cherry-pick, but doesn't apply to
the current 'next'- sorry, rebasing is a massive pita due to 1/5.
Thanks for reading.
-- Ram
Ramkumar Ramachandra (5):
sequencer: factor code out of revert builtin
sequencer: remove CHERRY_PICK_HEAD with sequencer state
sequencer: sequencer state is useless without todo
sequencer: handle single commit pick separately
sequencer: revert d3f4628e
builtin/revert.c | 821 +--------------------------------------
sequencer.c | 832 ++++++++++++++++++++++++++++++++++++++-
sequencer.h | 26 ++
t/t3510-cherry-pick-sequence.sh | 24 --
4 files changed, 847 insertions(+), 856 deletions(-)
--
1.7.6.351.gb35ac.dirty
^ permalink raw reply
* Re: [PATCH na/strtoimax] Compatibility: declare strtoimax() under NO_STRTOUMAX
From: Nix @ 2011-11-05 15:38 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Junio C Hamano, Git Mailing List
In-Reply-To: <4EB5583E.2030306@kdbg.org>
On 5 Nov 2011, Johannes Sixt said:
> Commit f696543d (Add strtoimax() compatibility function) introduced an
> implementation of the function, but forgot to add a declaration.
Oh, my apologies. (How did my testing miss that? No -Wall, I bet.)
--
NULL && (void)
^ permalink raw reply
* Re: share object storage for multiple clones of different repositories
From: Gelonida N @ 2011-11-05 16:20 UTC (permalink / raw)
To: git
In-Reply-To: <j93mu2$ce7$1@dough.gmane.org>
On 11/05/2011 05:06 PM, Gelonida N wrote:
> Thanks for both of your replies,
>
>
> I'll probably mix both approaches and try out how it works.
> I didn't know about git.git
> What is the best url for an intrudoction into git.git?
>
>
>>
>> Alternatively there's the git-new-workdir script in contrib/workdir in
>> git.git. Haven't tested it, but it seems like it does what you want.
>>
>> Frans
>
>
In an installed git (at least on Ubuntu) the contrib directory can be
found at /usr/share/doc/git/contrib
^ permalink raw reply
* Re: share object storage for multiple clones of different repositories
From: Gelonida N @ 2011-11-05 16:06 UTC (permalink / raw)
To: git
In-Reply-To: <op.v4gp4mai0aolir@keputer.lokaal>
Thanks for both of your replies,
I'll probably mix both approaches and try out how it works.
I didn't know about git.git
What is the best url for an intrudoction into git.git?
On 11/05/2011 08:37 AM, Frans Klaver wrote:
> On Sat, 05 Nov 2011 03:26:11 +0100, Junio C Hamano <gitster@pobox.com>
> wrote:
>
>> Gelonida N <gelonida@gmail.com> writes:
>>
>>> SHARED_STORAGE=$HOME/shared_storage
>>> mkdir $SHARED_STORAGE
>>>
>>> git clone remotehost1:repo1
>>> cd repo1
>>> rsync -av .git/objects $SHARED_REPO
>>
>> Up to this part it is probably OK. Repeat that for all your local
>> repositories to collect all objects in $HOME/shared_storage.
>>
>> After doing that, do this in all of your local repositories:
>>
>> rm -rf .git/objects
>> mkdir -p .git/objects/info
>> echo $HOME/shared/storage >.git/objects/info/alternates
>>
>> The reason why nobody should follow your original recipe is because any
>> "git gc"/"git repack" in any of your local repositories would break
>> others
>> with that approach.
>
>
> Alternatively there's the git-new-workdir script in contrib/workdir in
> git.git. Haven't tested it, but it seems like it does what you want.
>
> Frans
^ permalink raw reply
* Re: [PATCH] pull: introduce a pull.rebase option to enable --rebase
From: Johannes Schindelin @ 2011-11-05 15:55 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason
Cc: git, Junio C Hamano, Eric Herman, Sverre Rabbelier,
Fernando Vezzosi
In-Reply-To: <1320507358-3407-1-git-send-email-avarab@gmail.com>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 674 bytes --]
Hi,
On Sat, 5 Nov 2011, Ævar Arnfjörð Bjarmason wrote:
> Currently we either need to set branch.<name>.rebase for existing
> branches if we'd like "git pull" to mean "git pull --rebase", or have
> the forethought of setting "branch.autosetuprebase" before we create the
> branch.
>
> But there's no way to globally configure "git pull" to mean "git pull
> --rebase" for existing branches, introduce a "pull.rebase" option to do
> that.
>
> This option will be considered at a lower priority than
> branch.<name>.rebase, i.e. we could set pull.rebase=true and
> branch.<name>.rebase=false and the latter configuration option would
> win.
Nice.
Ciao,
Johannes
^ permalink raw reply
* [PATCH 2/1] gc --auto: warn gc will soon run, give users a chance to run manually
From: Fernando Vezzosi @ 2011-11-05 10:33 UTC (permalink / raw)
To: git
In-Reply-To: <20111105140529.3A6CE9004A@inscatolati.net>
Signed-off-by: Fernando Vezzosi <buccia@repnz.net>
---
Rebased Nguyễn's patch on top of mine.
builtin/gc.c | 19 +++++++++++++++++--
1 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index 65b6616..ca620e3 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -25,8 +25,10 @@ static const char * const builtin_gc_usage[] = {
static int pack_refs = 1;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
+static int gc_warn_auto_threshold = 6600;
static int gc_auto_pack_limit = 50;
static int gc_auto_warn_only = 0;
+static int gc_warn_auto_pack_limit = 45;
static const char *prune_expire = "2.weeks.ago";
#define MAX_ADD 10
@@ -51,10 +53,12 @@ static int gc_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "gc.auto")) {
gc_auto_threshold = git_config_int(var, value);
+ gc_warn_auto_threshold = gc_auto_threshold - 100;
return 0;
}
if (!strcmp(var, "gc.autopacklimit")) {
gc_auto_pack_limit = git_config_int(var, value);
+ gc_warn_auto_pack_limit = gc_auto_pack_limit - 5;
return 0;
}
if (!strcmp(var, "gc.pruneexpire")) {
@@ -123,7 +127,13 @@ static int too_many_loose_objects(void)
}
}
closedir(dir);
- return needed;
+ if (needed)
+ return 1;
+
+ auto_threshold = (gc_warn_auto_threshold + 255) / 256;
+ if (num_loose > auto_threshold)
+ warning(_("Too many loose objects. \"git gc\" will soon run automatically"));
+ return 0;
}
static int too_many_packs(void)
@@ -146,7 +156,12 @@ static int too_many_packs(void)
*/
cnt++;
}
- return gc_auto_pack_limit <= cnt;
+ if (gc_auto_pack_limit <= cnt)
+ return 1;
+
+ if (gc_warn_auto_pack_limit <= cnt)
+ warning(_("Too many packs, \"git gc\" will soon run automatically."));
+ return 0;
}
static int need_to_gc(void)
--
1.7.5.3
^ permalink raw reply related
* [PATCH na/strtoimax] Compatibility: declare strtoimax() under NO_STRTOUMAX
From: Johannes Sixt @ 2011-11-05 15:37 UTC (permalink / raw)
To: Nick Alcock; +Cc: Junio C Hamano, Git Mailing List
Commit f696543d (Add strtoimax() compatibility function) introduced an
implementation of the function, but forgot to add a declaration.
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
git-compat-util.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/git-compat-util.h b/git-compat-util.h
index feb6f8e..4efef46 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -354,6 +354,8 @@ extern size_t gitstrlcpy(char *, const char *, size_t);
#ifdef NO_STRTOUMAX
#define strtoumax gitstrtoumax
extern uintmax_t gitstrtoumax(const char *, char **, int);
+#define strtoimax gitstrtoimax
+extern intmax_t gitstrtoimax(const char *, char **, int);
#endif
#ifdef NO_STRTOK_R
--
1.7.7.1.1608.gd424d
^ permalink raw reply related
* [PATCH] pull: introduce a pull.rebase option to enable --rebase
From: Ævar Arnfjörð Bjarmason @ 2011-11-05 15:35 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Eric Herman, Sverre Rabbelier, Fernando Vezzosi,
Johannes Schindelin, Ævar Arnfjörð Bjarmason
Currently we either need to set branch.<name>.rebase for existing
branches if we'd like "git pull" to mean "git pull --rebase", or have
the forethought of setting "branch.autosetuprebase" before we create
the branch.
But there's no way to globally configure "git pull" to mean "git pull
--rebase" for existing branches, introduce a "pull.rebase" option to
do that.
This option will be considered at a lower priority than
branch.<name>.rebase, i.e. we could set pull.rebase=true and
branch.<name>.rebase=false and the latter configuration option would
win.
Reviewed-by: Sverre Rabbelier <srabbelier@gmail.com>
Reviewed-by: Fernando Vezzosi <buccia@repnz.net>
Reviewed-by: Eric Herman <eric@freesa.org>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
Documentation/config.txt | 14 +++++++++++++-
Documentation/git-pull.txt | 2 +-
git-pull.sh | 4 ++++
t/t5520-pull.sh | 24 ++++++++++++++++++++++--
4 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5a841da..b2d7d92 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -677,7 +677,9 @@ branch.<name>.mergeoptions::
branch.<name>.rebase::
When true, rebase the branch <name> on top of the fetched branch,
instead of merging the default branch from the default remote when
- "git pull" is run.
+ "git pull" is run. See "pull.rebase" for doing this in a non
+ branch-specific manner.
+
*NOTE*: this is a possibly dangerous operation; do *not* use
it unless you understand the implications (see linkgit:git-rebase[1]
for details).
@@ -1590,6 +1592,16 @@ pretty.<name>::
Note that an alias with the same name as a built-in format
will be silently ignored.
+pull.rebase::
+ When true, rebase branches on top of the fetched branch, instead
+ of merging the default branch from the default remote when "git
+ pull" is run. See "branch.<name>.rebase" for setting this on a
+ per-branch basis.
+
+ *NOTE*: this is a possibly dangerous operation; do *not* use
+ it unless you understand the implications (see linkgit:git-rebase[1]
+ for details).
+
pull.octopus::
The default merge strategy to use when pulling multiple branches
at once.
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index e1da468..0f18ec8 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -108,7 +108,7 @@ include::merge-options.txt[]
fetched, the rebase uses that information to avoid rebasing
non-local changes.
+
-See `branch.<name>.rebase` and `branch.autosetuprebase` in
+See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
linkgit:git-config[1] if you want to make `git pull` always use
`{litdd}rebase` instead of merging.
+
diff --git a/git-pull.sh b/git-pull.sh
index 9868a0b..24b6b7c 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -44,6 +44,10 @@ merge_args=
curr_branch=$(git symbolic-ref -q HEAD)
curr_branch_short="${curr_branch#refs/heads/}"
rebase=$(git config --bool branch.$curr_branch_short.rebase)
+if test -z "$rebase"
+then
+ rebase=$(git config --bool pull.rebase)
+fi
dry_run=
while :
do
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 0e5eb67..ecc4fdc 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -94,16 +94,36 @@ test_expect_success '--rebase' '
test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
test new = $(git show HEAD:file2)
'
+test_expect_success 'pull.rebase' '
+ git reset --hard before-rebase &&
+ git config --bool pull.rebase true &&
+ test_when_finished "git config --unset pull.rebase" &&
+ git pull . copy &&
+ test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
+ test new = $(git show HEAD:file2)
+'
test_expect_success 'branch.to-rebase.rebase' '
git reset --hard before-rebase &&
- git config branch.to-rebase.rebase 1 &&
+ git config --bool branch.to-rebase.rebase true &&
+ test_when_finished "git config --unset branch.to-rebase.rebase" &&
git pull . copy &&
- git config branch.to-rebase.rebase 0 &&
test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
test new = $(git show HEAD:file2)
'
+test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
+ git reset --hard before-rebase &&
+ git config --bool pull.rebase true &&
+ test_when_finished "git config --unset pull.rebase" &&
+ git config --bool branch.to-rebase.rebase false &&
+ test_when_finished "git config --unset branch.to-rebase.rebase" &&
+ git pull . copy &&
+ git config --unset branch.to-rebase.rebase &&
+ test $(git rev-parse HEAD^) != $(git rev-parse copy) &&
+ test new = $(git show HEAD:file2)
+'
+
test_expect_success '--rebase with rebased upstream' '
git remote add -f me . &&
--
1.7.7
^ permalink raw reply related
* [PATCH 2/1] gc --auto: warn gc will soon run, give users a chance to run manually
From: Fernando Vezzosi @ 2011-11-05 10:33 UTC (permalink / raw)
To: git
In-Reply-To: <20111105140529.3A6CE9004A@inscatolati.net>
Signed-off-by: Fernando Vezzosi <buccia@repnz.net>
---
Rebased Nguyễn's patch on top of mine.
builtin/gc.c | 19 +++++++++++++++++--
1 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index 65b6616..ca620e3 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -25,8 +25,10 @@ static const char * const builtin_gc_usage[] = {
static int pack_refs = 1;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
+static int gc_warn_auto_threshold = 6600;
static int gc_auto_pack_limit = 50;
static int gc_auto_warn_only = 0;
+static int gc_warn_auto_pack_limit = 45;
static const char *prune_expire = "2.weeks.ago";
#define MAX_ADD 10
@@ -51,10 +53,12 @@ static int gc_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "gc.auto")) {
gc_auto_threshold = git_config_int(var, value);
+ gc_warn_auto_threshold = gc_auto_threshold - 100;
return 0;
}
if (!strcmp(var, "gc.autopacklimit")) {
gc_auto_pack_limit = git_config_int(var, value);
+ gc_warn_auto_pack_limit = gc_auto_pack_limit - 5;
return 0;
}
if (!strcmp(var, "gc.pruneexpire")) {
@@ -123,7 +127,13 @@ static int too_many_loose_objects(void)
}
}
closedir(dir);
- return needed;
+ if (needed)
+ return 1;
+
+ auto_threshold = (gc_warn_auto_threshold + 255) / 256;
+ if (num_loose > auto_threshold)
+ warning(_("Too many loose objects. \"git gc\" will soon run automatically"));
+ return 0;
}
static int too_many_packs(void)
@@ -146,7 +156,12 @@ static int too_many_packs(void)
*/
cnt++;
}
- return gc_auto_pack_limit <= cnt;
+ if (gc_auto_pack_limit <= cnt)
+ return 1;
+
+ if (gc_warn_auto_pack_limit <= cnt)
+ warning(_("Too many packs, \"git gc\" will soon run automatically."));
+ return 0;
}
static int need_to_gc(void)
--
1.7.5.3
^ permalink raw reply related
* Re: [PATCH] Introduce gc.autowarnonly config option
From: Sverre Rabbelier @ 2011-11-05 14:22 UTC (permalink / raw)
To: Fernando Vezzosi, Nguyễn Thái Ngọc Duy; +Cc: git
In-Reply-To: <20111105140529.3A6CE9004A@inscatolati.net>
Heya,
On Sat, Nov 5, 2011 at 14:39, Fernando Vezzosi <buccia@repnz.net> wrote:
> When `git gc --auto` would detect need for garbage collection to run, it
> would just run. With this patch, enabling gc.autowarnonly will instead
> make it just emit a warning.
>
> Reviewed-by: Sverre Rabbelier <srabbelier@gmail.com>
> Signed-off-by: Fernando Vezzosi <buccia@repnz.net>
Highly relevant considering recent (3 hours ago) patch that instead
adds a warning that gc will happen soon.
--
Cheers,
Sverre Rabbelier
^ permalink raw reply
* [PATCH] Introduce gc.autowarnonly config option
From: Fernando Vezzosi @ 2011-11-05 13:39 UTC (permalink / raw)
To: git
When `git gc --auto` would detect need for garbage collection to run, it
would just run. With this patch, enabling gc.autowarnonly will instead
make it just emit a warning.
Reviewed-by: Sverre Rabbelier <srabbelier@gmail.com>
Signed-off-by: Fernando Vezzosi <buccia@repnz.net>
---
builtin/gc.c | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index 0498094..65b6616 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -26,6 +26,7 @@ static int pack_refs = 1;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
+static int gc_auto_warn_only = 0;
static const char *prune_expire = "2.weeks.ago";
#define MAX_ADD 10
@@ -64,6 +65,10 @@ static int gc_config(const char *var, const char *value, void *cb)
}
return git_config_string(&prune_expire, var, value);
}
+ if (!strcmp(var, "gc.autowarnonly")) {
+ gc_auto_warn_only = git_config_bool(var, value);
+ return 0;
+ }
return git_default_config(var, value, cb);
}
@@ -219,6 +224,15 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
*/
if (!need_to_gc())
return 0;
+
+ if (gc_auto_warn_only){
+ fprintf(stderr,
+ _("Pack the repository for optimum performance by running\n"
+ "\"git gc\" manually. See "
+ "\"git help gc\" for more information.\n"));
+ return 0;
+ }
+
if (quiet)
fprintf(stderr, _("Auto packing the repository for optimum performance.\n"));
else
--
1.7.5.3
^ permalink raw reply related
* [PATCH] grep: detect number of CPUs for thread spawning
From: Ævar Arnfjörð Bjarmason @ 2011-11-05 14:16 UTC (permalink / raw)
To: git
Cc: Junio C Hamano, Eric Herman, Sverre Rabbelier, Fernando Vezzosi,
Ævar Arnfjörð Bjarmason
From: Eric Herman <eric@freesa.org>
Change the number of threads that we spawn from a hardcoded value of
"8" to what online_cpus() returns.
Back in v1.7.0-rc1~19^2 when threaded grep was introduced the number
of threads was hardcoded at compile time to 8, but this value was only
used if online_cpus() returned greater than 1.
However just using 8 threads regardless of the actual number of CPUs
is inefficient if we have more than 8 CPUs, and potentially wasteful
if we have fewer than 8 CPUs.
The array holding the threads is now being allocated at runtime
instead of being allocated statically. We free the array further down
the line in the wait_all() function; this is OK since the allocation
and freeing is guarded by the global "use_threads" variable, which is
based on the return value of online_cpus().
Reviewed-by: Sverre Rabbelier <srabbelier@gmail.com>
Reviewed-by: Fernando Vezzosi <buccia@repnz.net>
Signed-off-by: Eric Herman <eric@freesa.org>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
builtin/grep.c | 17 +++++++++++------
1 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/builtin/grep.c b/builtin/grep.c
index 3d7329d..307a253 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -27,8 +27,8 @@ static char const * const grep_usage[] = {
static int use_threads = 1;
#ifndef NO_PTHREADS
-#define THREADS 8
-static pthread_t threads[THREADS];
+static int nthreads;
+static pthread_t *threads;
static void *load_sha1(const unsigned char *sha1, unsigned long *size,
const char *name);
@@ -36,7 +36,7 @@ static void *load_file(const char *filename, size_t *sz);
enum work_type {WORK_SHA1, WORK_FILE};
-/* We use one producer thread and THREADS consumer
+/* We use one producer thread and online_cpus() consumer
* threads. The producer adds struct work_items to 'todo' and the
* consumers pick work items from the same array.
*/
@@ -254,6 +254,8 @@ static void start_threads(struct grep_opt *opt)
{
int i;
+ threads = xmalloc(nthreads * sizeof(pthread_t));
+
pthread_mutex_init(&grep_mutex, NULL);
pthread_mutex_init(&read_sha1_mutex, NULL);
pthread_cond_init(&cond_add, NULL);
@@ -264,7 +266,7 @@ static void start_threads(struct grep_opt *opt)
strbuf_init(&todo[i].out, 0);
}
- for (i = 0; i < ARRAY_SIZE(threads); i++) {
+ for (i = 0; i < nthreads; i++) {
int err;
struct grep_opt *o = grep_opt_dup(opt);
o->output = strbuf_out;
@@ -295,7 +297,7 @@ static int wait_all(void)
pthread_cond_broadcast(&cond_add);
grep_unlock();
- for (i = 0; i < ARRAY_SIZE(threads); i++) {
+ for (i = 0; i < nthreads; i++) {
void *h;
pthread_join(threads[i], &h);
hit |= (int) (intptr_t) h;
@@ -307,6 +309,8 @@ static int wait_all(void)
pthread_cond_destroy(&cond_write);
pthread_cond_destroy(&cond_result);
+ free(threads);
+
return hit;
}
#else /* !NO_PTHREADS */
@@ -1001,7 +1005,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
opt.regflags |= REG_ICASE;
#ifndef NO_PTHREADS
- if (online_cpus() == 1 || !grep_threads_ok(&opt))
+ nthreads = online_cpus();
+ if (nthreads == 1 || !grep_threads_ok(&opt))
use_threads = 0;
if (use_threads) {
--
1.7.7
^ permalink raw reply related
* [PATCH] Add abbreviated commit hash to rebase conflict message
From: Sverre Rabbelier @ 2011-11-05 14:02 UTC (permalink / raw)
To: Ævar Arnfjörð Bjarmason, Jonas Flodén,
Junio C Hamano, Eric Herman
Cc: Sverre Rabbelier
Also move the $msgnum to a more sensible location.
Before:
Patch failed at 0001 msg
After:
Patch 0001 failed at [da65151] msg
Reviewed-by: Eric Herman <eric@freesa.org>
Reviewed-by: Fernando Vezzosi <buccia@repnz.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Sverre Rabbelier <srabbelier@gmail.com>
---
git-am.sh | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/git-am.sh b/git-am.sh
index 9042432..9d70588 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -837,7 +837,8 @@ did you forget to use 'git add'?"
fi
if test $apply_status != 0
then
- eval_gettextln 'Patch failed at $msgnum $FIRSTLINE'
+ abbrev_commit=$(git log -1 --pretty=%h $commit)
+ eval_gettextln 'Patch $msgnum failed at [$abbrev_commit] $FIRSTLINE'
stop_here_user_resolve $this
fi
--
1.7.8.rc0.36.g67522.dirty
^ permalink raw reply related
* [PATCH] prune: show progress while marking reachable objects
From: Nguyễn Thái Ngọc Duy @ 2011-11-05 12:00 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Jeff King, Nguyễn Thái Ngọc Duy
prune already shows progress meter while pruning. The marking part may
take a few seconds or more, depending on repository size. Show
progress meter during this time too.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/prune.c | 6 +++++-
builtin/reflog.c | 2 +-
reachable.c | 14 ++++++++++----
reachable.h | 3 ++-
4 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/builtin/prune.c b/builtin/prune.c
index e65690b..6b39d3f 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -5,6 +5,7 @@
#include "builtin.h"
#include "reachable.h"
#include "parse-options.h"
+#include "progress.h"
#include "dir.h"
static const char * const prune_usage[] = {
@@ -124,6 +125,7 @@ static void remove_temporary_files(const char *path)
int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
+ struct progress *progress;
const struct option options[] = {
OPT__DRY_RUN(&show_only, "do not remove, show only"),
OPT__VERBOSE(&verbose, "report pruned objects"),
@@ -152,7 +154,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
else
die("unrecognized argument: %s", name);
}
- mark_reachable_objects(&revs, 1);
+ progress = start_progress_delay("Checking connectivity", 0, 0, 2);
+ mark_reachable_objects(&revs, 1, progress);
+ stop_progress(&progress);
prune_object_dir(get_object_directory());
prune_packed_objects(show_only);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 3a9c80f..062d7da 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -647,7 +647,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
init_revisions(&cb.revs, prefix);
if (cb.verbose)
printf("Marking reachable objects...");
- mark_reachable_objects(&cb.revs, 0);
+ mark_reachable_objects(&cb.revs, 0, NULL);
if (cb.verbose)
putchar('\n');
}
diff --git a/reachable.c b/reachable.c
index 3fc6b1d..293d37d 100644
--- a/reachable.c
+++ b/reachable.c
@@ -7,6 +7,7 @@
#include "revision.h"
#include "reachable.h"
#include "cache-tree.h"
+#include "progress.h"
static void process_blob(struct blob *blob,
struct object_array *p,
@@ -81,21 +82,25 @@ static void process_tag(struct tag *tag, struct object_array *p, const char *nam
add_object(tag->tagged, p, NULL, name);
}
-static void walk_commit_list(struct rev_info *revs)
+static void walk_commit_list(struct rev_info *revs, struct progress *progress)
{
int i;
struct commit *commit;
struct object_array objects = OBJECT_ARRAY_INIT;
+ uint32_t count = 0;
/* Walk all commits, process their trees */
- while ((commit = get_revision(revs)) != NULL)
+ while ((commit = get_revision(revs)) != NULL) {
process_tree(commit->tree, &objects, NULL, "");
+ display_progress(progress, ++count);
+ }
/* Then walk all the pending objects, recursively processing them too */
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *pending = revs->pending.objects + i;
struct object *obj = pending->item;
const char *name = pending->name;
+ display_progress(progress, ++count);
if (obj->type == OBJ_TAG) {
process_tag((struct tag *) obj, &objects, name);
continue;
@@ -191,7 +196,8 @@ static void add_cache_refs(struct rev_info *revs)
add_cache_tree(active_cache_tree, revs);
}
-void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
+void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
+ struct progress *progress)
{
/*
* Set up revision parsing, and mark us as being interested
@@ -217,5 +223,5 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
*/
if (prepare_revision_walk(revs))
die("revision walk setup failed");
- walk_commit_list(revs);
+ walk_commit_list(revs, progress);
}
diff --git a/reachable.h b/reachable.h
index 4075181..5d082ad 100644
--- a/reachable.h
+++ b/reachable.h
@@ -1,6 +1,7 @@
#ifndef REACHEABLE_H
#define REACHEABLE_H
-extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog);
+struct progress;
+extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog, struct progress *);
#endif
--
1.7.4.74.g639db
^ permalink raw reply related
* Re: aliases causing “Permission denied” error in git v1.7
From: Алексей Данченков @ 2011-11-05 11:51 UTC (permalink / raw)
To: git
In-Reply-To: <7vhb2jbopp.fsf@alter.siamese.dyndns.org>
Thank you very much, Jeff and Junio!
Not only I discovered more than one directory mentioned in the $PATH,
but missing, but also learned a lot, including the strace tool.
Cheers, Alexei
2011/11/5 Junio C Hamano <gitster@pobox.com>:
> Jeff King <peff@peff.net> writes:
>
> I do not know why my response is not showing up in the list archive, but
> the complaint was that "git co" works for him and "sudo git co" does not.
>
> I suspect that Alexsey will see differences between these:
>
> $ sh -c 'echo $PATH $HOME'
> $ sudo sh -c 'echo $PATH $HOME'
>
> Most likely, the latter would say /root/bin and /root; if an element in
> the $PATH is unreadable, git would say "cannot exec 'git-co': Permission denied".
>
>
>
^ permalink raw reply
* [PATCH] gc --auto: warn gc will soon run, give users a chance to run manually
From: Nguyễn Thái Ngọc Duy @ 2011-11-05 10:33 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
I hate it every single time git hangs because gc is activated.
Opening another terminal is an option but I would lose all terminal
settings I have on the current one (e.g. access to suspended vim
sessions).
I don't think gc_warn_* need their own config vars. Hopefully
hardcoded offset is good enough.
builtin/gc.c | 19 +++++++++++++++++--
1 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/builtin/gc.c b/builtin/gc.c
index 0498094..1f4555e 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -25,7 +25,9 @@ static const char * const builtin_gc_usage[] = {
static int pack_refs = 1;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
+static int gc_warn_auto_threshold = 6600;
static int gc_auto_pack_limit = 50;
+static int gc_warn_auto_pack_limit = 45;
static const char *prune_expire = "2.weeks.ago";
#define MAX_ADD 10
@@ -50,10 +52,12 @@ static int gc_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "gc.auto")) {
gc_auto_threshold = git_config_int(var, value);
+ gc_warn_auto_threshold = gc_auto_threshold - 100;
return 0;
}
if (!strcmp(var, "gc.autopacklimit")) {
gc_auto_pack_limit = git_config_int(var, value);
+ gc_warn_auto_pack_limit = gc_auto_pack_limit - 5;
return 0;
}
if (!strcmp(var, "gc.pruneexpire")) {
@@ -118,7 +122,13 @@ static int too_many_loose_objects(void)
}
}
closedir(dir);
- return needed;
+ if (needed)
+ return 1;
+
+ auto_threshold = (gc_warn_auto_threshold + 255) / 256;
+ if (num_loose > auto_threshold)
+ warning(_("Too many loose objects. \"git gc\" will soon run automatically"));
+ return 0;
}
static int too_many_packs(void)
@@ -141,7 +151,12 @@ static int too_many_packs(void)
*/
cnt++;
}
- return gc_auto_pack_limit <= cnt;
+ if (gc_auto_pack_limit <= cnt)
+ return 1;
+
+ if (gc_warn_auto_pack_limit <= cnt)
+ warning(_("Too many packs, \"git gc\" will soon run automatically."));
+ return 0;
}
static int need_to_gc(void)
--
1.7.4.74.g639db
^ permalink raw reply related
* Re: New Feature wanted: Is it possible to let git clone continue last break point?
From: Clemens Buchacher @ 2011-11-05 10:00 UTC (permalink / raw)
To: Shawn Pearce
Cc: Johannes Sixt, Jeff King, Junio C Hamano, Jonathan Nieder,
netroby, Git Mail List, Tomas Carnecky
In-Reply-To: <CAJo=hJtsiEEHA33CQn1MCvb7vFv7uEF+U292YgBa7EWv7P8Jng@mail.gmail.com>
On Fri, Nov 04, 2011 at 07:22:20AM -0700, Shawn Pearce wrote:
> On Fri, Nov 4, 2011 at 02:35, Johannes Sixt <j.sixt@viscovery.net> wrote:
> > Am 11/4/2011 9:56, schrieb Clemens Buchacher:
> >> Cache ... not the pack but the information
> >> to re-create it...
> >
> > It has been discussed. It doesn't work. Because with threaded pack
> > generation, the resulting pack is not deterministic.
So let the client disable it, if they'd rather have a resumeable
fetch than a fast one.
Sorry if I'm being obstinate here. But I don't understand the
problem and I can't find an explanation in related discussions.
> The information to create a pack for a repository with 2M objects
> (e.g. Linux kernel tree) is *at least* 152M of data. This is just a
> first order approximation of what it takes to write out the 2M SHA-1s,
> along with say a 4 byte length so you can find given an offset
> provided by the client roughly where to resumse in the object stream.
> This is like 25% of the pack size itself. Ouch.
Sorry, I should not have said HAVEs. All we need is the common
commits, and the sha1s of the WANTed branch heads at the time of
the initial fetch. That shouldn't be more than 10 or so in typical
cases.
> This data is still insufficient to resume from. A correct solution
> would allow you to resume in the middle of an object, which means we
> also need to store some sort of indicator of which representation was
> chosen from an existing pack file for object reuse. Which adds more
> data to the stream. And then there is the not so simple problem of how
> to resume in the middle of an object that was being recompressed on
> the fly, such as a large loose object.
How often does the "representation chosen from an existing pack
file for object reuse" change? Long term determinism is a problem,
yes. But I see no reason why it should not work for this short-term
case. So long as the pack is created by one particular git and libz
version, and for this particular consecutive run of fetches, we do
not need to store anything about the pack. The client downloads n
MB of data until the drop. To resume, the client says it already
has n MB of data.
No?
Clemens
^ permalink raw reply
* Re: [PATCH 00/10] Pulling signed tag
From: Nguyen Thai Ngoc Duy @ 2011-11-05 9:27 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Robin H. Johnson
In-Reply-To: <1320472900-6601-1-git-send-email-gitster@pobox.com>
On Sat, Nov 5, 2011 at 1:01 PM, Junio C Hamano <gitster@pobox.com> wrote:
> This is my fourth iteration to solve the "how can we allow authenticity of
> work by contributors to be validated by the integrator and leave enough
> information for later audit by third parties" topic. What is unusual is
> that this is not a fourth re-roll of one approach re-re-re-polished, but
> these four are all based on different design.
>
> This round is based on Linus's "let the integrator pull signed tag from
> the contributor" design.
That thread is really long and I may have missed pieces here and
there. My understanding is that this topic now, while supports kernel
development model, would not address the problem gentoo has, which is
described by Robbin in the same thread [1]. Is that correct?
[1] http://thread.gmane.org/gmane.linux.ide/50518/focus=1210629
--
Duy
^ permalink raw reply
* Re: [PATCHv2] Add options to specify snapshot file name, prefix
From: Jakub Narebski @ 2011-11-05 9:18 UTC (permalink / raw)
To: Thomas Guyot-Sionnest; +Cc: git
In-Reply-To: <201111050947.15440.jnareb@gmail.com>
Jakub Narebski wrote:
> So should we expect a re-roll?
By the way, those new query parameters ('sn' and 'sp') should IMHO
be documented in Documentation/gitweb.txt i.e. gitweb(1) manpage.
Also, shouldn't you infer snapshot format from snapshot name
("git archive" does part of that... but not the compression part)?
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: How do I get a squashed diff for review
From: David Aguilar @ 2011-11-05 9:15 UTC (permalink / raw)
To: Alexander Usov; +Cc: git, Roland Kaufmann
In-Reply-To: <CAH_EFyZ_0JB0-5cw-6VEJkfJhSjbmA=3upByQ3YpmnVSvR+9Pg@mail.gmail.com>
On Fri, Nov 04, 2011 at 07:15:01PM +0000, Alexander Usov wrote:
> Hi,
>
> I'm wondering if there is an easy way to get a squashed diff of the
> changes done on the feature branch for review.
> In the simple cases (where feature branch is linear) there is an
> absolutely fantastic way to get a patch for review:
> git diff master...feature
>
> However if the feature branch happened to be long-lived and had
> mainline merged into it it's not going to work -- the
> resulting diff would contain changes from the merge. The way we are
> doing things now is to merge master into it
> once more and then diff, however this is somewhat cumbersome. Is there
> easier way to do it?
"git diff A...B" is equivalent to "git diff <merge-base A B> B".
The merge-base can be found with "git merge-base A B"
and is simply the common ancestor of A and B.
Diffing against the merge base (which doesn't contain the merged
work done in master) is why you're seeing the merges in the diff.
It sounds like you want the simpler form of "diff" which doesn't
do any merge-base calculation.
e.g. "git diff A B" and its synonymn "git diff A..B".
> And while we are on the topic -- is there a tool for git similar to "bzr qdiff"?
> It's a simple graphical diff viewer with 2 nice features -- it shows
> complete diff (of multiple files) in a single window and
> has a checkbox to switch between diff-only & full-text modes.
> I have seen difftool, but it seems to work on per-file basis, and
> something like "vi <(git diff ...)" lacks the easy way to
> switch into full-text mode.
difftool is a wrapper around specialized diff tools, so the
ability to switch from diff to full view is tool-dependent.
A contrib "git-dirdiff" script was posted to the list recently.
It builds upon diff tools that can diff directory trees.
http://thread.gmane.org/gmane.comp.version-control.git/184528
There may be a newer version of this script, too. Roland would
know for sure...
--
David
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox