git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCH] Option to revert order of parents in merge commit
@ 2012-11-23  8:35 Kacper Kornet
  2012-11-24  2:58 ` Junio C Hamano
  0 siblings, 1 reply; 5+ messages in thread
From: Kacper Kornet @ 2012-11-23  8:35 UTC (permalink / raw)
  To: git

When the changes are pushed upstream, and in the meantime someone else
updated upstream branch git advises to use git pull. This results in
history:

     ---A---B---C--
         \     /
          D---E

where B is my commit. D, E are commits pushed by someone else when I was
working on B. However sometimes the following history is preferable:

    ---A---D---C'--
        \     /
          -B-

The difference between C and C' is the order of parents. Presently to
obtain it, instead of git pull, one needs to do (assuming that I am on
the master branch):

git fetch origin
git branch tmp_branch
git reset --hard origin/master
git merge tmp_branch

Reverting from wrong pull is more cumbersome. It would be simpler if git
merge learn an option to reverse order of parents in the produced
commits, so one could do:

git fetch origin
git merge --revert-order origin/master

The following patch is an attempt to implement this idea.

Signed-off-by: Kacper Kornet <draenog@pld-linux.org>
---

I'm not 100% percent sure that it is a good idea. But it would make life
of our developers easier and produced nicer history then using git pull.
Git pull seems to written for a case of single maintainer who gathers
contributions from other developers and incorporates them in master
branch. However in my opinion it doesn't produce best history when many
developers modify the canonical repository. 

 builtin/commit.c | 22 ++++++++++++++--------
 builtin/merge.c  | 16 ++++++++++++----
 commit.c         | 11 +++++++++++
 commit.h         |  2 ++
 4 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 1dd2ec5..ab2b844 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1427,7 +1427,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	unsigned char sha1[20];
 	struct ref_lock *ref_lock;
 	struct commit_list *parents = NULL, **pptr = &parents;
-	struct stat statbuf;
 	int allow_fast_forward = 1;
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
@@ -1478,10 +1477,21 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	} else if (whence == FROM_MERGE) {
 		struct strbuf m = STRBUF_INIT;
 		FILE *fp;
+		int reversed_order=0;
 
 		if (!reflog_msg)
 			reflog_msg = "commit (merge)";
-		pptr = &commit_list_insert(current_head, pptr)->next;
+		if((fp = fopen(git_path("MERGE_MODE"), "r"))) {
+			while (strbuf_getline(&m, fp, '\n') != EOF) {
+				if (!strcmp(m.buf, "no-ff"))
+					allow_fast_forward = 0;
+				if (!strcmp(m.buf, "reversed-order"))
+					reversed_order = 1;
+			}
+			fclose(fp);
+		}
+		if (!reversed_order)
+			pptr = &commit_list_insert(current_head, pptr)->next;
 		fp = fopen(git_path("MERGE_HEAD"), "r");
 		if (fp == NULL)
 			die_errno(_("could not open '%s' for reading"),
@@ -1496,12 +1506,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		}
 		fclose(fp);
 		strbuf_release(&m);
-		if (!stat(git_path("MERGE_MODE"), &statbuf)) {
-			if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
-				die_errno(_("could not read MERGE_MODE"));
-			if (!strcmp(sb.buf, "no-ff"))
-				allow_fast_forward = 0;
-		}
+		if (reversed_order)
+			pptr = &commit_list_insert(current_head, pptr)->next;
 		if (allow_fast_forward)
 			parents = reduce_heads(parents);
 	} else {
diff --git a/builtin/merge.c b/builtin/merge.c
index a96e8ea..8d0ed18 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -65,6 +65,7 @@ static int abort_current_merge;
 static int show_progress = -1;
 static int default_to_upstream;
 static const char *sign_commit;
+static int reversed_order=0;
 
 static struct strategy all_strategy[] = {
 	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -213,6 +214,7 @@ static struct option builtin_merge_options[] = {
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
 	OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
+	OPT_BOOLEAN(0, "revert-order", &reversed_order, N_("reverse order of parents")),
 	OPT_END()
 };
 
@@ -822,9 +824,9 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 
 	write_tree_trivial(result_tree);
 	printf(_("Wonderful.\n"));
-	parent->item = head;
+	parent->item = reversed_order ? remoteheads->item : head;
 	parent->next = xmalloc(sizeof(*parent->next));
-	parent->next->item = remoteheads->item;
+	parent->next->item = reversed_order ? head : remoteheads->item;
 	parent->next->next = NULL;
 	prepare_to_commit(remoteheads);
 	if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
@@ -848,8 +850,12 @@ static int finish_automerge(struct commit *head,
 
 	free_commit_list(common);
 	parents = remoteheads;
-	if (!head_subsumed || !allow_fast_forward)
+	if (!head_subsumed || !allow_fast_forward) {
+	    if (reversed_order )
+		commit_list_insert_end(head, &parents);
+	    else
 		commit_list_insert(head, &parents);
+	}
 	strbuf_addch(&merge_msg, '\n');
 	prepare_to_commit(remoteheads);
 	if (commit_tree(&merge_msg, result_tree, parents, result_commit,
@@ -994,7 +1000,9 @@ static void write_merge_state(struct commit_list *remoteheads)
 		die_errno(_("Could not open '%s' for writing"), filename);
 	strbuf_reset(&buf);
 	if (!allow_fast_forward)
-		strbuf_addf(&buf, "no-ff");
+		strbuf_addf(&buf, "no-ff\n");
+	if (reversed_order)
+		strbuf_addf(&buf, "reversed-order\n");
 	if (write_in_full(fd, buf.buf, buf.len) != buf.len)
 		die_errno(_("Could not write to '%s'"), filename);
 	close(fd);
diff --git a/commit.c b/commit.c
index e8eb0ae..6e58994 100644
--- a/commit.c
+++ b/commit.c
@@ -363,6 +363,17 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *
 	return new_list;
 }
 
+struct commit_list *commit_list_insert_end(struct commit *item, struct commit_list **list_p)
+{
+	struct commit_list *list_iter = *list_p;
+	while(list_iter->next)
+		list_iter = list_iter->next;
+	list_iter->next = xmalloc(sizeof(*list_iter->next));
+	list_iter->next->item = item;
+	list_iter->next->next = NULL;
+	return *list_p;
+}
+
 unsigned commit_list_count(const struct commit_list *l)
 {
 	unsigned c = 0;
diff --git a/commit.h b/commit.h
index b6ad8f3..17ae5e5 100644
--- a/commit.h
+++ b/commit.h
@@ -53,6 +53,8 @@ int find_commit_subject(const char *commit_buffer, const char **subject);
 
 struct commit_list *commit_list_insert(struct commit *item,
 					struct commit_list **list);
+struct commit_list *commit_list_insert_end(struct commit *item,
+					struct commit_list **list);
 struct commit_list **commit_list_append(struct commit *commit,
 					struct commit_list **next);
 unsigned commit_list_count(const struct commit_list *l);
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC/PATCH] Option to revert order of parents in merge commit
  2012-11-23  8:35 [RFC/PATCH] Option to revert order of parents in merge commit Kacper Kornet
@ 2012-11-24  2:58 ` Junio C Hamano
  2012-11-26 12:42   ` Kacper Kornet
  0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2012-11-24  2:58 UTC (permalink / raw)
  To: Kacper Kornet; +Cc: git

Kacper Kornet <draenog@pld-linux.org> writes:

> The following patch is an attempt to implement this idea.

I think "revert" is a wrong word (implying you have already done
something and you are trying to defeat the effect of that old
something), and you meant to say "reverse" (i.e. the opposite of
normal) or something.

I am unsure about the usefulness of this, though.

After completing a topic on branch A, you would merge it to your own
copy of the integration branch (e.g. 'master') and try to push,
which may be rejected due to non-fast-forwardness:

    $ git checkout master
    $ git merge A
    $ git push

At that point, if you _care_ about the merge parent order, you could
do this (still on 'master'):

    $ git fetch origin
    $ git reset --hard origin/master
    $ git merge A
    $ test test test
    $ git push

With --reverse-parents, it would become:

    $ git pull --reverse-parents
    $ test test test
    $ git push

which certainly is shorter and looks simpler.  The workflow however
would encourage people to work directly on the master branch, which
is a bit of downside.

Is there any interaction between this "pull --reverse-parents"
change and possible conflict resolution when the command stops and
asks the user for help?  For example, whom should "--ours" and "-X
ours" refer to?  Us, or the upstream?

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC/PATCH] Option to revert order of parents in merge commit
  2012-11-24  2:58 ` Junio C Hamano
@ 2012-11-26 12:42   ` Kacper Kornet
  2012-11-26 17:58     ` Junio C Hamano
  0 siblings, 1 reply; 5+ messages in thread
From: Kacper Kornet @ 2012-11-26 12:42 UTC (permalink / raw)
  To: Junio C Hamano, git

On Fri, Nov 23, 2012 at 06:58:49PM -0800, Junio C Hamano wrote:
> Kacper Kornet <draenog@pld-linux.org> writes:

> > The following patch is an attempt to implement this idea.

> I think "revert" is a wrong word (implying you have already done
> something and you are trying to defeat the effect of that old
> something), and you meant to say "reverse" (i.e. the opposite of
> normal) or something.

You are right. Probably transpose is the best description what the patch
really does.

> I am unsure about the usefulness of this, though.

> After completing a topic on branch A, you would merge it to your own
> copy of the integration branch (e.g. 'master') and try to push,
> which may be rejected due to non-fast-forwardness:

>     $ git checkout master
>     $ git merge A
>     $ git push

> At that point, if you _care_ about the merge parent order, you could
> do this (still on 'master'):

>     $ git fetch origin
>     $ git reset --hard origin/master
>     $ git merge A
>     $ test test test
>     $ git push

> With --reverse-parents, it would become:

>     $ git pull --reverse-parents
>     $ test test test
>     $ git push

> which certainly is shorter and looks simpler.  The workflow however
> would encourage people to work directly on the master branch, which
> is a bit of downside.

Our developers work mainly on master branches. The project consists of
many thousands independent git repositories, and at the given time a
developer usually wants to make only one commit in the given repository
and push his changes upstream. So he usually doesn't care to make a
branch.  Then after failed pushed, one needs to add creation and removal
of temporary branch (see the commit message of the suggested patch).
The possibility to do git pull --reverse-parent would make the life
easier in this case.

> Is there any interaction between this "pull --reverse-parents"
> change and possible conflict resolution when the command stops and
> asks the user for help?  For example, whom should "--ours" and "-X
> ours" refer to?  Us, or the upstream?

The change of order of parents happens at the very last moment, so
"ours" in merge options is local version and "theirs" upstream.

-- 
  Kacper Kornet

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC/PATCH] Option to revert order of parents in merge commit
  2012-11-26 12:42   ` Kacper Kornet
@ 2012-11-26 17:58     ` Junio C Hamano
  2012-11-26 23:24       ` Aaron Schrab
  0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2012-11-26 17:58 UTC (permalink / raw)
  To: Kacper Kornet; +Cc: git

Kacper Kornet <draenog@pld-linux.org> writes:

>> Is there any interaction between this "pull --reverse-parents"
>> change and possible conflict resolution when the command stops and
>> asks the user for help?  For example, whom should "--ours" and "-X
>> ours" refer to?  Us, or the upstream?
>
> The change of order of parents happens at the very last moment, so
> "ours" in merge options is local version and "theirs" upstream.

That may be something that wants to go to the proposed commit log
message.  I am neutral on the "feature" (i.e. not against it but not
extremely enthusiastic about it either).

Thanks.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC/PATCH] Option to revert order of parents in merge commit
  2012-11-26 17:58     ` Junio C Hamano
@ 2012-11-26 23:24       ` Aaron Schrab
  0 siblings, 0 replies; 5+ messages in thread
From: Aaron Schrab @ 2012-11-26 23:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Kacper Kornet, git

At 09:58 -0800 26 Nov 2012, Junio C Hamano <gitster@pobox.com> wrote:
>Kacper Kornet <draenog@pld-linux.org> writes:
>> The change of order of parents happens at the very last moment, so
>> "ours" in merge options is local version and "theirs" upstream.
>
>That may be something that wants to go to the proposed commit log
>message.  I am neutral on the "feature" (i.e. not against it but not
>extremely enthusiastic about it either).

That should also be included in the (currently nonexistent) 
documentation of the proposed option.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-11-26 23:24 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-11-23  8:35 [RFC/PATCH] Option to revert order of parents in merge commit Kacper Kornet
2012-11-24  2:58 ` Junio C Hamano
2012-11-26 12:42   ` Kacper Kornet
2012-11-26 17:58     ` Junio C Hamano
2012-11-26 23:24       ` Aaron Schrab

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).