From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>, "Jeff King" <peff@peff.net>,
"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH 7/7] merge: add --rename-notes
Date: Wed, 20 Jan 2016 18:06:08 +0700 [thread overview]
Message-ID: <1453287968-26000-8-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1453287968-26000-1-git-send-email-pclouds@gmail.com>
For merge professionals, --rename-file is as bad a nightmare as
resolving conflicts because they may have to create a rename file for
every merge. --rename-notes takes advantage of rename notes to avoid that.
Because rename notes are between commit A and A^, not between A and its
merge base, we need to convert "rename path A to path B" to "rename blob
A' to blob B'" and hope that mere base still has that blob A'.
Merging notes this way is expensive. So it's cached in $GIT_DIR/rename-cache.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
builtin/merge.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/builtin/merge.c b/builtin/merge.c
index 95a6c26..6ad2e52 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -30,6 +30,7 @@
#include "fmt-merge-msg.h"
#include "gpg-interface.h"
#include "sequencer.h"
+#include "notes.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -69,6 +70,7 @@ static int default_to_upstream = 1;
static const char *sign_commit;
static const char *rename_file;
static struct strbuf manual_renames = STRBUF_INIT;
+static const char *rename_note_ref;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -228,6 +230,7 @@ static struct option builtin_merge_options[] = {
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
OPT_FILENAME(0, "rename-file", &rename_file, N_("--rename-file to diff")),
+ OPT_STRING(0, "rename-notes", &rename_note_ref, N_("note-ref"), N_("--rename-notes to diff")),
OPT_END()
};
@@ -1161,6 +1164,102 @@ static struct commit_list *collect_parents(struct commit *head_commit,
return remoteheads;
}
+static int merge_rename_note(const unsigned char *object_hash,
+ const unsigned char *note_hash,
+ char *note_path,
+ void *cb_data)
+{
+ struct strbuf *cache = cb_data;
+ struct strbuf sb = STRBUF_INIT;
+ const char *p, *end;
+ enum object_type type;
+ unsigned long size;
+ char *note;
+
+ note = read_sha1_file(note_hash, &type, &size);
+ if (type != OBJ_BLOB) {
+ free(note);
+ return 0;
+ }
+
+ p = note;
+ end = p + strlen(p);
+ while (p < end) {
+ const char *line_end = strchr(p, '\n');
+ const char *arrow = strstr(p, " => ");
+ const char *src = p, *dst;
+ struct object_id src_oid;
+ struct object_id dst_oid;
+
+ if (!line_end)
+ line_end = end;
+ p = line_end + 1;
+
+ if (!arrow || arrow >= line_end)
+ continue;
+
+ if (starts_with(src, "blob ") && src + 4 < arrow) {
+ strbuf_addf(cache, "%.*s\n",
+ (int)(line_end - src), src);
+ continue;
+ }
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s:%.*s", sha1_to_hex(object_hash),
+ (int)(arrow - src), src);
+ if (get_sha1(sb.buf, src_oid.hash))
+ continue;
+
+ dst = arrow + strlen(" => ");
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s:%.*s", sha1_to_hex(object_hash),
+ (int)(line_end - dst), dst);
+ if (get_sha1(sb.buf, dst_oid.hash))
+ continue;
+
+ strbuf_addf(cache, "blob %s => %s\n",
+ oid_to_hex(&src_oid),
+ oid_to_hex(&dst_oid));
+ }
+
+ return 0;
+}
+
+/*
+ * Traverse through the given notes tree, convert all "path to path"
+ * rename lines into "blob to blob" and return it. If cache_file is
+ * non-NULL, return it's content if still valid. Otherwise save the
+ * new content in it.
+ */
+static void merge_rename_notes(struct strbuf *cache,
+ struct notes_tree *t, const char *cache_file)
+{
+ struct object_id notes_oid;
+
+ if (cache_file) {
+ struct object_id cache_oid;
+
+ strbuf_reset(cache);
+ if (!resolve_ref_unsafe(t->ref, RESOLVE_REF_READING,
+ notes_oid.hash, NULL))
+ return;
+
+ if (strbuf_read_file(cache, cache_file, 0) > GIT_SHA1_HEXSZ + 1 &&
+ cache->buf[GIT_SHA1_HEXSZ] == '\n' &&
+ !get_oid_hex(cache->buf, &cache_oid) &&
+ !oidcmp(¬es_oid, &cache_oid)) {
+ strbuf_remove(cache, 0, GIT_SHA1_HEXSZ + 1);
+ return;
+ }
+ }
+
+ strbuf_reset(cache);
+ for_each_note(t, 0, merge_rename_note, cache);
+ if (cache_file && cache->len)
+ write_file(cache_file, "%s\n%s",
+ oid_to_hex(¬es_oid), cache->buf);
+}
+
int cmd_merge(int argc, const char **argv, const char *prefix)
{
unsigned char result_tree[20];
@@ -1260,10 +1359,25 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_merge_usage,
builtin_merge_options);
+ if (rename_file && rename_note_ref)
+ die(_("--rename-file and --rename-notes are incompatible"));
+
if (rename_file &&
strbuf_read_file(&manual_renames, rename_file, 0) == -1)
die(_("unable to read %s"), rename_file);
+ if (rename_note_ref) {
+ struct notes_tree rename_notes;
+ struct strbuf ref = STRBUF_INIT;
+
+ strbuf_addstr(&ref, rename_note_ref);
+ expand_notes_ref(&ref);
+ init_notes(&rename_notes, ref.buf, NULL, 0);
+ strbuf_release(&ref);
+ merge_rename_notes(&manual_renames, &rename_notes,
+ git_path("GIT_RENAME_CACHE"));
+ }
+
if (!head_commit) {
struct commit *remote_head;
/*
--
2.7.0.125.g9eec362
next prev parent reply other threads:[~2016-01-20 11:07 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-20 11:06 [PATCH 0/7] Diff rename, manual correction, round 2 Nguyễn Thái Ngọc Duy
2016-01-20 11:06 ` [PATCH 1/7] diff-no-index: do not take a redundant prefix argument Nguyễn Thái Ngọc Duy
2016-01-20 11:06 ` [PATCH 2/7] diff.c: take "prefix" argument in diff_opt_parse() Nguyễn Thái Ngọc Duy
2016-01-20 20:23 ` Junio C Hamano
2016-01-20 20:29 ` Jeff King
2016-01-20 21:49 ` Junio C Hamano
2016-01-21 11:48 ` Duy Nguyen
2016-01-21 23:01 ` Junio C Hamano
2016-01-20 11:06 ` [PATCH 3/7] diff: add --rename-file Nguyễn Thái Ngọc Duy
2016-01-20 22:44 ` Junio C Hamano
2016-01-20 22:47 ` Junio C Hamano
2016-01-20 11:06 ` [PATCH 4/7] log: add --rename-notes to correct renames per commit Nguyễn Thái Ngọc Duy
2016-01-20 23:29 ` Junio C Hamano
2016-01-22 1:00 ` Duy Nguyen
2016-01-20 11:06 ` [PATCH 5/7] merge: add --rename-file Nguyễn Thái Ngọc Duy
2016-01-20 11:06 ` [PATCH 6/7] diffcore-rename: allow to say "rename this blob to that blob" Nguyễn Thái Ngọc Duy
2016-01-20 11:06 ` Nguyễn Thái Ngọc Duy [this message]
2016-01-21 17:53 ` [PATCH 7/7] merge: add --rename-notes Junio C Hamano
2016-01-22 3:35 ` Duy Nguyen
2016-01-22 17:17 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1453287968-26000-8-git-send-email-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=peff@peff.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).