threads / discuss / 1917

Re: Please undo "Use git-merge instead of git-resolve in

Subject: Re: Please undo "Use git-merge instead of git-resolve in

## tl;dr

7 messages between Sep 22, 2005 and Sep 23, 2005.

replies: 6people: 4as markdown or json

Jon Loeliger· Sep 22, 2005, 19:12 UTC · lore
Linus calmly explained again:
Show 22 quoted lines
>
> The "origin" branch was already updated automatically if you used
> 
> 	git pull origin
> 
> However, if you used
> 
> 	git pull rsync:..../linus-2.6.git
> 
> then git would only have fetched the objects, and _not_ updated the 
> "origin" branch (because you didn't tell it about the "origin" branch). 
> 
> But you can trivially fix that by doing
> 
> 	git fetch origin
> 
> at this point, which will end up doing the equivalent of
> 
> 	git fetch rsync:..../linus-2.6.git master:origin
> 
> (depending on what the contents of your .git/remotes/origin are, of 
> course, the above is just an example).
This was a very helpful section for me.
Show 7 quoted lines
> For example, it _may_ just contain something like
> 
>	URL: rsync:..../linus2.6.git
> 
> in which case there is no explicit heads specified. In that case, you 
> really should specify _which_ of the heads you want to pull, and what the 
> target should be. 
In fact it contained just the "rsync:...." bits.
Show 6 quoted lines
> For example, you can do that with
>
>	git fetch linus master:linus
>
> which says: "fetch the branch 'master' from the repository described by 
> 'linus' (.git/remotes/linus) into _my_ branch 'linus'".

Just to be clear on the overloaded use of "linus" here, let me rewrite that:

	git fetch foo master:bar
    which says: "fetch the branch 'master' from the repository described
    by 'foo' (.git/remotes/foo) into _my_ branch 'bar'".
Right?
Show 6 quoted lines
> Now, you can specify that this is what you _always_ want to do, and
> actually set the branches in the ".git/remotes/linus" file explicitly.
> You can make your .git/remotes/linus file look like this instead:
>
>	URL: rsync:..../linus2.6.git
>	Pull: master:linus

OK. I think I get it. The concept you are trying to beat into my head is essentially the difference between a "complete" and an "incomplete" specifier. If there is enough specification to fetch and merge into a branch, you can. And there are a few different ways to do so:

    A) git fetch rsync:..../linus-2.6.git master:origin
or 
    B) git fetch linus master:linus
       with just the URL bits in the .git/remotes
or
    C)	git fetch linus
        with both URL and "Pull:" clauses in .git remotes
            URL: rsync:..../linus2.6.git
	    Pull: master:linus

If it is an incomplete specification (without branch names), you just can't do the whole sheebang. You have to stop after fetching the objects.

And add "merging" in the "git pull" cases.
> Now, this is where "dirty" comes in. The merge will fail if you have 
> changed any files that the merge wants to modify. So do a

D'oh. This is what bites me. Some of those diffs that come back to me in the linus tree are one that I originated and sent up stream in the first place. To get those to wash-out with the merge, I will have had to have had those committed in my branch, right? Otherwise they just look like dirty files -- never mind that they are (should be) the exact same bits coming back at me.

Show 6 quoted lines
> So do a
>
>	git diff --name-only HEAD
>
> to see if there are any changes, and commit them - or undo them - if so.
> Then try to pull again.
Ah hah.  OK.
I want to return to this for one last comment:
    > You can make your .git/remotes/linus file look like this instead:
    >
    >	URL: rsync:..../linus2.6.git
    >	Pull: master:linus

So, philosophically perhaps, there are several different reasons for having branches within a repository. One of them is to track external repositories as indicated above. But no-one (ie, no tool) currently constructs the .git/remotes files to have that layout, right? I mean, The One True editor can, but there is no git or Cogito command that says to construct the URL: and Pull: bits like that for the purposes of establishing a "track branch", right? Would it make sense to have a "--tracking" flag added to branch creation that trumped these bits up and promotes a simpler, or specific purpose branch?

jdl
Linus Torvalds· Sep 22, 2005, 21:22 UTC · re: Jon Loeliger · lore
On Thu, 22 Sep 2005, Jon Loeliger wrote:
Show 17 quoted lines
>
> > For example, you can do that with
> >
> >	git fetch linus master:linus
> >
> > which says: "fetch the branch 'master' from the repository described by 
> > 'linus' (.git/remotes/linus) into _my_ branch 'linus'".
> 
> Just to be clear on the overloaded use of "linus" here,
> let me rewrite that:
> 
> 	git fetch foo master:bar
> 
>     which says: "fetch the branch 'master' from the repository described
>     by 'foo' (.git/remotes/foo) into _my_ branch 'bar'".
> 
> Right?
Right.
Show 15 quoted lines
> OK.  I think I get it.  The concept you are trying to beat into
> my head is essentially the difference between a "complete" and
> an "incomplete" specifier.  If there is enough specification to
> fetch and merge into a branch, you can.  And there are a few
> different ways to do so:
> 
>     A) git fetch rsync:..../linus-2.6.git master:origin
> or 
>     B) git fetch linus master:linus
>        with just the URL bits in the .git/remotes
> or
>     C)	git fetch linus
>         with both URL and "Pull:" clauses in .git remotes
>             URL: rsync:..../linus2.6.git
> 	    Pull: master:linus
Absolutely.

Note that you can also override, so let's say that you have _both_ the "URL:" and the "Pull:" line in your "remotes" file, the "pull" line is still just a default, so you could do

	git fetch linus other:myother

and it would now use the URL line, but since you specified the branch by hand, it would ignore the default branch information, and instead fetch the "other" branch from the remote site into the "myother" local branch.

Show 8 quoted lines
> > Now, this is where "dirty" comes in. The merge will fail if you have 
> > changed any files that the merge wants to modify. So do a
> 
> D'oh.  This is what bites me.  Some of those diffs that
> come back to me in the linus tree are one that I originated
> and sent up stream in the first place.  To get those to
> wash-out with the merge, I will have had to have had those
> committed in my branch, right?

Yes. Then git will merge them for you automatically (if they are identical), or may ask you to fix up any merge rejects (if there are clashes).

Now, the fixing up any merges is pretty much exactly the same as under CVS - you'll get the same "<<<<" "====" ">>>>" markers.

>			  Otherwise they just look
> like dirty files -- never mind that they are (should be)
> the exact same bits coming back at me.
Right, but git doesn't know, and is too lazy to check. 

I _considered_ allowing a dirty file that happened to match the end result, but I never felt it was worth the complexity. It's nasty. And if it doesn't match, you really can't merge, because it's too dangerous to overwrite the users changes (even if you use some three-way merge and leave the rejects there).

Show 10 quoted lines
> I want to return to this for one last comment:
> 
>     > You can make your .git/remotes/linus file look like this instead:
>     >
>     >	URL: rsync:..../linus2.6.git
>     >	Pull: master:linus
> 
> So, philosophically perhaps, there are several different
> reasons for having branches within a repository.  One of
> them is to track external repositories as indicated above.
Yes.
> But no-one (ie, no tool) currently constructs the .git/remotes
> files to have that layout, right?

The "git clone" thing will construct the original "origin" file to that layout. That's fairly recent, though.

And I think "cg-branch" will do it these days - and that's very much the intent. Havign porcelain on top of git that hides this thing so that you don't have to edit configuration files by hand.

(Don't get me wrong: I think it's wonderful to have ASCII configuration files that you _can_ edit by hand, and it's the UNIX way of doing things, but I also think it's great to have front-end tools that allow doing these things without even having to understand the config file syntax etc).

		Linus
Linus Torvalds· Sep 22, 2005, 21:37 UTC · re: Linus Torvalds · lore
On Thu, 22 Sep 2005, Linus Torvalds wrote:
> 
> Now, the fixing up any merges is pretty much exactly the same as under 
> CVS - you'll get the same "<<<<" "====" ">>>>" markers.
I should have continued this. 

What you do is you just edit the end result to be what it should be (exactly as under CVS), and then you do "git commit --all" to commit the manual merge.

NOTE NOTE NOTE! This is one case where if you had dirty state in your tree before the merge, that you need to be careful. In that case you obviously must NOT use "--all", since that will commit all the dirty state - both your old dirty state _and_ your manual merge resolve. So if you had other changes in your tree that you don't want to commit, you need to do

	git commit <list manually merged files individually here>
so that it doesn't commit anything else. 
Oh. And while preparing the merge, you can use
	git diff

to see what the manual part was: the automated part of the merge will have updated index for the automatic side, so this will _not_ show the part of the merge that was done automatically for you.

Or, of you want to see _everything_ the merge has done, just do
	git diff HEAD

to see what the _total_ diff from your pre-merge head was to what is now in your tree. But that will be the diff for what you're merging - so it can be _huge_ if you're pulling my tree and merging everything I've accepted over the last week or two.

And if the merge ends up being too complex, and you decide that you need help and want to undo the partial merge entirely and not even try to fix it up, do

	git reset --hard

which will roll back the index and the working tree to your old HEAD (BUT LOOK OUT! IT WILL ALSO DESTROY ANY DIRTY STATE YOU HAD BEFORE THE MERGE!).

Basically: having dirty state in your tree and then ending up having to do 
a manual merge is a big pain. Not worth it. I just keep really small dirty 
stuff so that I don't mind blowing it away if I need to.
		Linus
Daniel Barkalow· Sep 22, 2005, 21:57 UTC · re: Linus Torvalds · lore
On Thu, 22 Sep 2005, Linus Torvalds wrote:
> Basically: having dirty state in your tree and then ending up having to do 
> a manual merge is a big pain. Not worth it. I just keep really small dirty 
> stuff so that I don't mind blowing it away if I need to.

Would it be worthwhile to have a flag to make git-read-tree abort rather than making a mess if you have any dirty state and the merge isn't completely automatic? It's certainly easy enough to write.

	-Daniel
*This .sig left intentionally blank*
Linus Torvalds· Sep 22, 2005, 22:05 UTC · re: Daniel Barkalow · lore
On Thu, 22 Sep 2005, Daniel Barkalow wrote:
> 
> Would it be worthwhile to have a flag to make git-read-tree abort rather 
> than making a mess if you have any dirty state and the merge isn't 
> completely automatic? It's certainly easy enough to write.
I don't think it would be wrong, necessarily.
On the other hand, it might be easier to just instead do a 
	git diff HEAD > .git/pre-merge-diff

and let it go at that. If the merge ends up being nasty, you can then just do

	git reset --hard
	git-apply .git/pre-merge-diff
or something.

I dunno. This is not something that has caused me a lot of headache. I certainly _hope_ that people generally don't keep a lot of dirty state around: I do it for truly small stuff that I don't care about.

Let's face it - if I cared about it, I'd have committed it (possibly to another branch). It's not like that's hard. So the dirty stuff really does tend to be only trivial things.

		Linus
Junio C Hamano· Sep 23, 2005, 08:33 UTC · re: Linus Torvalds · lore

[PATCH] Fix overzealous cleanliness check in git-merge

Being able to try multiple strategies and automatically picking one that seems to give less conflicting result may or may not much sense in practice. At least that should not force normal use case to additionally require the working tree to be fully clean. As Linus shouted, local changes do not matter unless they interfere with the merge.

This commit changes git-merge not to require a clean working tree. Only when we will iterate through more than one merge strategies, local changes are stashed away before trying the first merge, and restored before second and later merges are attempted.

The index file must be in sync with HEAD in any case -- otherwise the merge result would contain changes since HEAD that was done locally and registered in the index. This check is already enforced by three-way read-tree existing merge strategies use, but is done here as a safeguard as well.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Taking hint from your message, here is an attempt to fix the
 'incredibly broken' git-merge.  This does not make git-pull
 use it yet.
 git-merge.sh |   65 ++++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 49 insertions(+), 16 deletions(-)
abfefa19cf56a312d571ea36a491152ac7aef657
diff --git a/git-merge.sh b/git-merge.sh
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -18,8 +18,19 @@ all_strategies='recursive octopus resolv
 default_strategies='resolve octopus'
 use_strategies=
 
-dropheads() {
-	rm -f -- "$GIT_DIR/MERGE_HEAD" || exit 1
+dropsave() {
+	rm -f -- "$GIT_DIR/MERGE_HEAD" \
+		 "$GIT_DIR/MERGE_SAVE" || exit 1
+}
+
+savestate() {
+	git diff -r -z --name-only $head | cpio -0 -o >"$GIR_DIR/MERGE_SAVE"
+}
+
+restorestate() {
+	git reset --hard $head
+	cpio -iuv <"$GIT_DIR/MERGE_SAVE"
+	git-update-index --refresh >/dev/null
 }
 
 summary() {
@@ -92,7 +103,7 @@ case "$#,$common" in
 	# If head can reach all the merge then we are up to date.
 	# but first the most common case of merging one remote
 	echo "Already up-to-date. Yeeah!"
-	dropheads
+	dropsave
 	exit 0
 	;;
 1,"$head")
@@ -102,7 +113,7 @@ case "$#,$common" in
 	git-read-tree -u -m $head "$1" || exit 1
 	git-rev-parse --verify "$1^0" > "$GIT_DIR/HEAD"
 	summary "$1"
-	dropheads
+	dropsave
 	exit 0
 	;;
 1,*)
@@ -124,29 +135,51 @@ case "$#,$common" in
 	if test "$up_to_date" = t
 	then
 		echo "Already up-to-date. Yeeah!"
-		dropheads
+		dropsave
 		exit 0
 	fi
 	;;
 esac
 
-# At this point we need a real merge.  Require that the tree matches
-# exactly our head.
 
-git-update-index --refresh &&
-test '' = "`git-diff-index --cached --name-only $head`" || {
-	die "Need real merge but the working tree has local changes."
-}
+# At this point, we need a real merge.  No matter what strategy
+# we use, it would operate on the index, possibly affecting the
+# working tree, and when resolved cleanly, have the desired tree
+# in the index -- this means that the index must be in sync with
+# the $head commit.
+files=$(git-diff-index --cached --name-only $head) || exit
+if [ "$files" ]; then
+   echo >&2 "Dirty index: cannot merge (dirty: $files)"
+   exit 1
+fi
+
+case "$use_strategies" in
+?*' '?*)
+    # Stash away the local changes so that we can try more than one.
+    savestate
+    single_strategy=no
+    ;;
+*)
+    single_strategy=yes
+    ;;
+esac
 
 result_tree= best_cnt=-1 best_strategy= wt_strategy=
 for strategy in $use_strategies
 do
     test "$wt_strategy" = '' || {
 	echo "Rewinding the tree to pristine..."
-	git reset --hard $head
+	restorestate
     }
-    echo "Trying merge strategy $strategy..."
+    case "$single_strategy" in
+    no)
+	echo "Trying merge strategy $strategy..."
+	;;
+    esac
+
+    # Remember which strategy left the state in the working tree
     wt_strategy=$strategy
+
     git-merge-$strategy $common -- $head "$@" || {
 
 	# The backend exits with 1 when conflicts are left to be resolved,
@@ -185,14 +218,14 @@ then
     echo "Committed merge $result_commit, made by $wt_strategy."
     echo $result_commit >"$GIT_DIR/HEAD"
     summary $result_commit
-    dropheads
+    dropsave
     exit 0
 fi
 
 # Pick the result from the best strategy and have the user fix it up.
 case "$best_strategy" in
 '')
-	git reset --hard $head
+	restorestate
 	die "No merge strategy handled the merge."
 	;;
 "$wt_strategy")
@@ -200,7 +233,7 @@ case "$best_strategy" in
 	;;
 *)
 	echo "Rewinding the tree to pristine..."
-	git reset --hard $head
+	restorestate
 	echo "Using the $best_strategy to prepare resolving by hand."
 	git-merge-$best_strategy $common -- $head "$@"
 	;;
Junio C Hamano· Sep 23, 2005, 09:01 UTC · re: Linus Torvalds · lore

[PATCH] Use git-merge in git-pull (second try).

This again makes git-pull to use git-merge, so that different merge strategy can be specified from the command line. Without explicit strategy parameter, it defaults to git-merge-resolve if only one remote is pulled, and git-merge-octopus otherwise, to keep the default behaviour of the command the same as the original.

Also this brings another usability measure: -n flag from the command line, if given, is passed to git-merge to prevent it from running the diffstat at the end of the merge.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 I tested this at least once with a dirty working tree ;-).  The
 main difference from the one that was backed out is that only
 one strategy is used by default -- this is to avoid any
 'stashing away old states' done on the git-merge side.
 git-pull.sh |   48 ++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 44 insertions(+), 4 deletions(-)
9c078a16523a2aae224a99e70f41eeb3d05a470c
diff --git a/git-pull.sh b/git-pull.sh
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -6,6 +6,38 @@
 
 . git-sh-setup || die "Not a git archive"
 
+usage () {
+    die "git pull [-n] [-s strategy]... <repo> <head>..."
+}
+
+strategy_args= no_summary=
+while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac
+do
+	case "$1" in
+	-n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
+		--no-summa|--no-summar|--no-summary)
+		no_summary=-n ;;
+	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
+		--strateg=*|--strategy=*|\
+	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
+		case "$#,$1" in
+		*,*=*)
+			strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+		1,*)
+			usage ;;
+		*)
+			strategy="$2"
+			shift ;;
+		esac
+		strategy_args="${strategy_args}-s $strategy "
+		;;
+	-*)
+		usage
+		;;
+	esac
+	shift
+done
+
 orig_head=$(cat "$GIT_DIR/HEAD") || die "Pulling into a black hole?"
 git-fetch --update-head-ok "$@" || exit 1
 
@@ -31,11 +63,19 @@ case "$merge_head" in
 	echo >&2 "No changes."
 	exit 0
 	;;
-*' '?*)
-	echo >&2 "Pulling more than one heads; making an Octopus."
-	exec git-octopus
+?*' '?*)
+	strategy_default_args='-s octopus'
+	;;
+*)
+	strategy_default_args='-s resolve'
+	;;
+esac
+
+case "$strategy_args" in
+'')
+	strategy_args=$strategy_default_args
 	;;
 esac
 
 merge_name=$(git-fmt-merge-msg <"$GIT_DIR/FETCH_HEAD")
-git-resolve "$(cat "$GIT_DIR"/HEAD)" $merge_head "$merge_name"
+git-merge $no_summary $strategy_args "$merge_name" HEAD $merge_head

← back to recent threads