threads / patch / 27612

patchPreserve ORIG_HEAD if already up-to-date with remote.

Subject: [PATCH] Preserve ORIG_HEAD if already up-to-date with remote.

## tl;dr

4 messages between Jun 13, 2011 and Jul 17, 2011. Diffs are folded; open one to read it.

replies: 3people: 2as markdown or json

Kunal Gangakhedkar· Jun 13, 2011, 07:35 UTC · lore

By default, the ORIG_HEAD ref is moved to point to HEAD during a merge. If there are no changes (i.e. if the local is in sync with remote), it becomes difficult to see the last set of changes as the range of commits is lost.

This is especially true when the pull is performed via a cronjob or a script.

Following this mail is a patch that tries to address the problem by not updating the ORIG_HEAD ref when it detects an empty merge (i.e. local is in sync with the remote). That way, one can still do diffstat/log between ORIG_HEAD..HEAD.

It's still possible to revert to old behavior with: o. --force-update-orig-head cmd line option o. merge.forceupdateorighead config option

Please review the patch and let me know if: a) it makes sense to have this functionality b) if the patch looks OK

Please let me know if there are any changes required. I'll write the test cases and send them in another mail later.

Thanks, Kunal

Kunal Gangakhedkar (1):
  merge: Preserve ORIG_HEAD if already up-to-date with remote.
 builtin/merge.c |   26 ++++++++++++++++++++++++--
 1 files changed, 24 insertions(+), 2 deletions(-)
-- 
1.7.6.rc1.2.g20c4a.dirty
Kunal Gangakhedkar· Jun 13, 2011, 07:35 UTC · re: Kunal Gangakhedkar · lore

[PATCH] merge: Preserve ORIG_HEAD if already up-to-date with remote.

Do not update ORIG_HEAD ref to current HEAD if the repo is already in-sync with the remote.

Otherwise, it becomes difficult to keep track of last set of changes.

With this patch, it's possible to do a diffstat/log for last set of changes even after a pull/merge that returns 'Already up-to-date'. This is especially useful when the pull is performed via a cronjob/script.

The old behaviour can still be forced via: o. --force-update-orig-head cmd line option o. merge.forceupdateorighead config option

Signed-off-by: Kunal Gangakhedkar <kunal.gangakhedkar@gmail.com>
---
 builtin/merge.c |   26 ++++++++++++++++++++++++--
 1 files changed, 24 insertions(+), 2 deletions(-)
Show changes to builtin/merge.c +24 −2
diff --git a/builtin/merge.c b/builtin/merge.c
index 325891e..6ac26f0 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -63,6 +63,7 @@ static int allow_rerere_auto;
 static int abort_current_merge;
 static int show_progress = -1;
 static int default_to_upstream;
+static int force_update_orig_head = 0;
 
 static struct strategy all_strategy[] = {
 	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -206,6 +207,8 @@ static struct option builtin_merge_options[] = {
 	OPT_BOOLEAN(0, "abort", &abort_current_merge,
 		"abort the current in-progress merge"),
 	OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
+	OPT_BOOLEAN(0, "force-update-orig-head", &force_update_orig_head,
+			"force-update ORIG_HEAD even if in sync with remote"),
 	OPT_END()
 };
 
@@ -563,7 +566,11 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 	} else if (!strcmp(k, "merge.defaulttoupstream")) {
 		default_to_upstream = git_config_bool(k, v);
 		return 0;
+	} else if (!strcmp(k, "merge.forceupdateorighead")) {
+		force_update_orig_head = git_config_bool(k, v);
+		return 0;
 	}
+
 	return git_diff_ui_config(k, v, cb);
 }
 
@@ -1017,6 +1024,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	struct commit_list *common = NULL;
 	const char *best_strategy = NULL, *wt_strategy = NULL;
 	struct commit_list **remotes = &remoteheads;
+	int orig_head_updated = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1212,8 +1220,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		free(list);
 	}
 
-	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
-		DIE_ON_ERR);
+	if (force_update_orig_head) {
+		update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+			DIE_ON_ERR);
+		orig_head_updated = 1;
+	}
 
 	if (!common)
 		; /* No common ancestors found. We need a real merge. */
@@ -1254,6 +1265,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		finish(o->sha1, msg.buf);
 		drop_save();
+		if (!orig_head_updated) {
+			update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+				DIE_ON_ERR);
+			orig_head_updated = 1;
+		}
 		return 0;
 	} else if (!remoteheads->next && common->next)
 		;
@@ -1306,6 +1322,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		}
 	}
 
+	if (!orig_head_updated) {
+		update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+			DIE_ON_ERR);
+		orig_head_updated = 1;
+	}
+
 	if (fast_forward_only)
 		die(_("Not possible to fast-forward, aborting."));
 
-- 
1.7.6.rc1.2.g20c4a.dirty
Junio C Hamano· Jun 14, 2011, 23:14 UTC · re: Kunal Gangakhedkar · lore

Re: [PATCH] merge: Preserve ORIG_HEAD if already up-to-date with remote.

Kunal Gangakhedkar <kunal.gangakhedkar@gmail.com> writes:
Show 7 quoted lines
> Do not update ORIG_HEAD ref to current HEAD if the repo is already
> in-sync with the remote.
>
> Otherwise, it becomes difficult to keep track of last set of changes.
>
> With this patch, it's possible to do a diffstat/log for last set of
> changes even after a pull/merge that returns 'Already up-to-date'.

I am not sure what you mean. If you are behind remote A and remote B is behind remote A, i.e.

	You -- (fast forwards) --> B -- (fast forwards) --> A
then you should be able to expect these:
	: point zero
	$ git pull A
        $ git diff/log ORIG_HEAD.. ;# shows how your tree and history
        			    # are updated wrt point zero above
	: point one
	$ git pull B
        $ git diff/log ORIG_HEAD.. ;# shows how your tree and history
        			    # are updated wrt point one above

If your patch is to stop recording ORIG_HEAD for the second "git pull" above, I fail to see how that could be a good change.

If you want to drive "pull" from a script (e.g. cron) and want to have precise control of what happens depending on how HEAD is updated, your script has enough freedom and flexibility to do so before running "pull", I think. E.g.

	. $(git --exec-path)/git-sh-setup
	CURRENT=$(git rev-parse HEAD^0) || die "Eh? No Head?"
        git pull || die "pull fails"
        UPDATED=$(git rev-parse HEAD^0)
        if test "$CURRENT" = "$UPDATED"
        then
		exit 0; # nothing happened
	fi
	git shortlog --no-merges $CURRENT..$UPDATED
        git diff --stat --summary $CURRENT..$UPDATED
Kunal Gangakhedkar· Jul 17, 2011, 16:31 UTC · re: Junio C Hamano · lore

Re: [PATCH] merge: Preserve ORIG_HEAD if already up-to-date with remote.

On Wednesday 15 Jun 2011 4:44:28 am Junio C Hamano wrote:
Show 46 quoted lines
> Kunal Gangakhedkar <kunal.gangakhedkar@gmail.com> writes:
> > Do not update ORIG_HEAD ref to current HEAD if the repo is already
> > in-sync with the remote.
> > 
> > Otherwise, it becomes difficult to keep track of last set of changes.
> > 
> > With this patch, it's possible to do a diffstat/log for last set of
> > changes even after a pull/merge that returns 'Already up-to-date'.
> 
> I am not sure what you mean.  If you are behind remote A and remote B is
> behind remote A, i.e.
> 
> 	You -- (fast forwards) --> B -- (fast forwards) --> A
> 
> then you should be able to expect these:
> 	: point zero
> 
> 	$ git pull A
>         $ git diff/log ORIG_HEAD.. ;# shows how your tree and history
>         			    # are updated wrt point zero above
> 
> 	: point one
> 
> 	$ git pull B
>         $ git diff/log ORIG_HEAD.. ;# shows how your tree and history
>         			    # are updated wrt point one above
> 
> If your patch is to stop recording ORIG_HEAD for the second "git pull"
> above, I fail to see how that could be a good change.
> 
> If you want to drive "pull" from a script (e.g. cron) and want to have
> precise control of what happens depending on how HEAD is updated, your
> script has enough freedom and flexibility to do so before running "pull",
> I think.  E.g.
> 
> 	. $(git --exec-path)/git-sh-setup
> 
> 	CURRENT=$(git rev-parse HEAD^0) || die "Eh? No Head?"
>         git pull || die "pull fails"
>         UPDATED=$(git rev-parse HEAD^0)
>         if test "$CURRENT" = "$UPDATED"
>         then
> 		exit 0; # nothing happened
> 	fi
> 	git shortlog --no-merges $CURRENT..$UPDATED
>         git diff --stat --summary $CURRENT..$UPDATED
Sorry, got caught up in butt-load of work..

Well, that's not what I meant. The default diffstat at the end of merge/ff/pull already gives the required info.

The problem comes when the remote _does not_ have updates since last pull. With a 'git pull' next time around (i.e. with no updates in the remote), ORIG_HEAD is moved to the current HEAD and the range of commits in the last changeset are lost. That is, I can no longer figure out the contents of the last changeset.

To give an example, let's track the following sequence:
	$ git clone git://url/of/super-awesome/project.git  	(0)
		.... hack hack hack in remote ....
		.... remote is fast-forwarded ...
	$ git pull									(1)
		... you got the changes ....
		.... hack hack hack in remote ....
		.... remote is fast-forwarded ...
	$ git pull									(2)
		... you got the changes ....
		... now, everybody is on vacation, so, no updates in remote ...
	$ git pull									(3)
		... you get 'Already up-to-date.' message ...
		... everybody is still on vacation, so, no updates in remote  ...
	$ git pull									(4) 
		... you get 'Already up-to-date.' message ...
In the current situation, at the end of (3) above, ORIG_HEAD is moved to the 
then HEAD. So, after (3), if I try to do 
	git diff/log ORIG_HEAD..
I'd get an empty changeset - i.e. I've lost the *last updated* changeset 
information.

What my patch does is keep ORIG_HEAD at its current state in (3) - so that changeset info is still available after (3) until there is an update in tracked branch of remote. At which point, ORIG_HEAD will be forwarded again to the then HEAD and HEAD will point to the newly merged HEAD to reflect the changes from remote.

This way, the *latest* changeset is always available for inspection whether 
there's been an update in remote or not. I can safely do 
	git diff/log ORIG_HEAD..
after (4) as well and I'd still get the required information.

I've included config/cmdline options in the patch to force-update ORIG_HEAD so that current behavior is retained.

Please let me know if it makes sense.
I've written a simple test script for the functionality, but I'm not sure how 
to name it. Currently, in my working copy, it's called 
	t5555-merge-preserve-orig-head.sh
If that's fine, I'll send it in another patch for review. Otherwise, please 
suggest a good name for it.

Thanks, Kunal

← back to recent threads