Git development
 help / color / mirror / Atom feed
* What's cooking in git.git (topics)
From: Junio C Hamano @ 2008-07-08  2:46 UTC (permalink / raw)
  To: git
In-Reply-To: <7vlk0ffhw3.fsf@gitster.siamese.dyndns.org>

Here are the topics that have been cooking.  Commits prefixed
with '-' are only in 'pu' while commits prefixed with '+' are
in 'next'.

The topics list the commits in reverse chronological order.  The topics
meant to be applied to the maintenance series have "maint-" in their
names.

It already is beginning to become clear what 1.6.0 will look like.  What's
already in 'next' all are well intentioned (I do not guarantee they are
already bug-free --- that is what cooking them in 'next' is for) and are
good set of feature enhancements.  Bigger changes will be:

 * Port for MinGW.

 * With the default Makefile settings, most of the programs will be
   installed outside your $PATH, except for "git", "gitk", "git-gui" and
   some server side programs that need to be accessible for technical
   reasons.  Invoking a git subcommand as "git-xyzzy" from the command
   line has been deprecated since early 2006 (and officially announced in
   1.5.4 release notes); use of them from your scripts after adding
   output from "git --exec-path" to the $PATH will still be supported in
   1.6.0, but users are again strongly encouraged to adjust their
   scripts to use "git xyzzy" form, as we will stop installing
   "git-xyzzy" hardlinks for built-in commands in later releases.

 * git-merge will be rewritten in C.

 * default pack and idx versions will be updated as scheduled for some
   time ago.

 * GIT_CONFIG, which was only documented as affecting "git config", but
   actually affected all git commands, now only affects "git config".
   GIT_LOCAL_CONFIG, also only documented as affecting "git config" and
   not different from GIT_CONFIG in a useful way, is removed.

----------------------------------------------------------------
[New Topics]

* jc/rebase-orig-head (Mon Jul 7 00:16:38 2008 -0700) 1 commit
 + Teach "am" and "rebase" to mark the original position with
   ORIG_HEAD

* sb/sequencer (Tue Jul 1 04:38:34 2008 +0200) 4 commits
 . Migrate git-am to use git-sequencer
 . Add git-sequencer test suite (t3350)
 . Add git-sequencer prototype documentation
 . Add git-sequencer shell prototype

* js/pick-root (Fri Jul 4 16:19:52 2008 +0100) 1 commit
 + Allow cherry-picking root commits

* ab/bundle (Sat Jul 5 17:26:40 2008 -0400) 1 commit
 + Teach git-bundle to read revision arguments from stdin like git-
   rev-list.

----------------------------------------------------------------
[Will merge to master soon]

* js/apply-root (Sun Jul 6 18:36:01 2008 -0700) 3 commits
 + git-apply --directory: make --root more similar to GNU diff
 + apply --root: thinkofix.
 + Teach "git apply" to prepend a prefix with "--root=<root>"

* jc/reflog-expire (Sat Jun 28 22:24:49 2008 -0700) 2 commits
 + Make default expiration period of reflog used for stash infinite
 + Per-ref reflog expiry configuration

As 1.6.0 will be a good time to make backward incompatible changes, the
tip commit makes the default expiry period of stash 'never', unless you
configure them to expire explicitly using gc.refs/stash.* variables.
Needs consensus, but I am guessing that enough people would want stash
that does not expire.

* jk/pager-config (Thu Jul 3 07:46:57 2008 -0400) 1 commit
 + Allow per-command pager config

----------------------------------------------------------------
[Actively Cooking]

* sg/stash-k-i (Fri Jun 27 16:37:15 2008 +0200) 1 commit
 + stash: introduce 'stash save --keep-index' option

One weakness of our "partial commit" workflow support used to be that the
user can incrementally build what is to be committed in the index but that
state cannot be tested as a whole in the working tree.  This allows you to
temporarily stash the remaining changes in the working tree so that the
index state before running "stash save --keep-index" can be seen in the
working tree to be tested and then committed.

* am/stash-branch (Mon Jul 7 02:50:10 2008 +0530) 2 commits
 + Add a test for "git stash branch"
 + Implement "git stash branch <newbranch> <stash>"

Creates a new branch out of the stashed state, after returning from the
interrupt that forced you to create the stash in the first place.

* tr/add-i-e (Thu Jul 3 00:00:00 2008 +0200) 3 commits
 + git-add--interactive: manual hunk editing mode
 + git-add--interactive: remove hunk coalescing
 + git-add--interactive: replace hunk recounting with apply --recount

Adds 'e/dit' action to interactive add command.

* jc/report-tracking (Sun Jul 6 02:54:56 2008 -0700) 5 commits
 + branch -r -v: do not spit out garbage
 + stat_tracking_info(): clear object flags used during counting
 + git-branch -v: show the remote tracking statistics
 + git-status: show the remote tracking statistics
 + Refactor "tracking statistics" code used by "git checkout"

Makes the "your branch is ahead of the tracked one by N commits" logic and
messages available to other commands; status and branch are updated.

* jc/merge-theirs (Mon Jun 30 22:18:57 2008 -0700) 5 commits
 + Make "subtree" part more orthogonal to the rest of merge-
   recursive.
 + Teach git-pull to pass -X<option> to git-merge
 + Teach git-merge to pass -X<option> to the backend strategy module
 + git-merge-recursive-{ours,theirs}
 + git-merge-file --ours, --theirs

Punting a merge by discarding your own work in conflicting parts but still
salvaging the parts that are cleanly automerged.  It is likely that this
will result in nonsense mishmash, but somehow often people want this, so
here they are.  The interface to the backends is updated so that you can
say "git merge -Xours -Xsubtree=foo/bar/baz -s recursive other" now.

The -X<option> part may change, Dscho mentions that a single-letter -X
that take stuck option is against syntax rules, and I think he's right.

This is more "because we can", not "because we need to".

* mv/merge-in-c (Mon Jul 7 19:24:20 2008 +0200) 15 commits
 - Build in merge
 + Fix t7601-merge-pull-config.sh on AIX
 + git-commit-tree: make it usable from other builtins
 + Add new test case to ensure git-merge prepends the custom merge
   message
 + Add new test case to ensure git-merge reduces octopus parents when
   possible
 + Introduce reduce_heads()
 + Introduce get_merge_bases_many()
 + Add new test to ensure git-merge handles more than 25 refs.
 + Introduce get_octopus_merge_bases() in commit.c
 + git-fmt-merge-msg: make it usable from other builtins
 + Move read_cache_unmerged() to read-cache.c
 + Add new test to ensure git-merge handles pull.twohead and
   pull.octopus
 + Move parse-options's skip_prefix() to git-compat-util.h
 + Move commit_list_count() to commit.c
 + Move split_cmdline() to alias.c

----------------------------------------------------------------
[Graduated to "master"]

* js/import-zip (Mon Jun 30 19:50:44 2008 +0100) 1 commit
 + Add another fast-import example, this time for .zip files

* db/no-git-config (Mon Jun 30 03:37:47 2008 -0400) 1 commit
 + Only use GIT_CONFIG in "git config", not other programs

* dr/ceiling (Mon May 19 23:49:34 2008 -0700) 4 commits
 + Eliminate an unnecessary chdir("..")
 + Add support for GIT_CEILING_DIRECTORIES
 + Fold test-absolute-path into test-path-utils
 + Implement normalize_absolute_path

* jc/rerere (Sun Jun 22 02:04:31 2008 -0700) 5 commits
 + rerere.autoupdate
 + t4200: fix rerere test
 + rerere: remove dubious "tail_optimization"
 + git-rerere: detect unparsable conflicts
 + rerere: rerere_created_at() and has_resolution() abstraction

A new configuration will allow paths that have been resolved cleanly by
rerere to be updated in the index automatically.

* js/maint-daemon-syslog (Thu Jul 3 16:27:24 2008 +0100) 1 commit
 + git daemon: avoid calling syslog() from a signal handler

Meant for 'maint' as well.

----------------------------------------------------------------
[On Hold]

* sg/merge-options (Sun Apr 6 03:23:47 2008 +0200) 1 commit
 + merge: remove deprecated summary and diffstat options and config
   variables

This was previously in "will be in master soon" category, but it turns out
that the synonyms to the ones this one deletes are fairly new invention
that happend in 1.5.6 timeframe, and we cannot do this just yet.  Perhaps
in 1.7.0.

* jc/dashless (Thu Jun 26 16:43:34 2008 -0700) 2 commits
 + Revert "Make clients ask for "git program" over ssh and local
   transport"
 + Make clients ask for "git program" over ssh and local transport

This is the "botched" one.  Will be resurrected during 1.7.0 or 1.8.0
timeframe.

* jk/renamelimit (Sat May 3 13:58:42 2008 -0700) 1 commit
 - diff: enable "too large a rename" warning when -M/-C is explicitly
   asked for

This would be the right thing to do for command line use, but gitk will be
hit due to tcl/tk's limitation, so I am holding this back for now.

----------------------------------------------------------------
[Stalled/Needs more work]

* jc/grafts (Wed Jul 2 17:14:12 2008 -0700) 1 commit
 - [BROKEN wrt shallow clones] Ignore graft during object transfer

Cloning or fetching from a repository from grafts did not send objects
that are hidden by grafts, but the commits in the resulting repository do
need these to pass fsck.  This fixes object transfer to ignore grafts.

Another fix is needed to git-prune so that it ignores grafts but treats
commits that are mentioned in grafts as reachable.

* ph/parseopt-step-blame (Tue Jun 24 11:12:12 2008 +0200) 7 commits
 - Migrate git-blame to parse-option partially.
 + parse-opt: add PARSE_OPT_KEEP_ARGV0 parser option.
 + parse-opt: fake short strings for callers to believe in.
 + parse-opt: do not print errors on unknown options, return -2
   intead.
 + parse-opt: create parse_options_step.
 + parse-opt: Export a non NORETURN usage dumper.
 + parse-opt: have parse_options_{start,end}.

I recall Pierre said something about cleaning up the last one when he
finds time, but other than that vague recollection, I lost track of this
series.  I am tempted to fork a few topics off of the penúltimo one to
convert a few more commands as examples and merge the result to 'next'.

* jc/blame (Wed Jun 4 22:58:40 2008 -0700) 7 commits
 - blame: show "previous" information in --porcelain/--incremental
   format
 - git-blame: refactor code to emit "porcelain format" output
 + git-blame --reverse
 + builtin-blame.c: allow more than 16 parents
 + builtin-blame.c: move prepare_final() into a separate function.
 + rev-list --children
 + revision traversal: --children option

The blame that finds where each line in the original lines moved to.  This
may help a GSoC project that wants to gather statistical overview of the
history.  The final presentation may need tweaking (see the log message of
the commit ""git-blame --reverse" on the series).

The tip two commits are for peeling to see what's behind the blamed
commit, which we should be able to separate out into an independent topic
from the rest.

^ permalink raw reply

* What's in git.git (stable)
From: Junio C Hamano @ 2008-07-08  2:46 UTC (permalink / raw)
  To: git
In-Reply-To: <7vr6a7fhwh.fsf@gitster.siamese.dyndns.org>

* The 'maint' branch has these fixes since 1.5.6.2.

Alex Riesen (1):
  git-clone: remove leftover debugging fprintf().

Brian Hetro (5):
  builtin-log.c: Use 'git_config_string' to get 'format.subjectprefix' and
    'format.suffix'
  convert.c: Use 'git_config_string' to get 'smudge' and 'clean'
  diff.c: Use 'git_config_string' to get 'diff.external'
  http.c: Use 'git_config_string' to clean up SSL config.
  builtin-commit.c: Use 'git_config_string' to get 'commit.template'

Christian Couder (1):
  Fix "config_error_nonbool" used with value instead of key

Gerrit Pape (1):
  git-svn.perl: workaround assertions in svn library 1.5.0

Junio C Hamano (3):
  attribute documentation: keep EXAMPLE at end
  clone -q: honor "quiet" option over native transports.
  mailinfo: feed the correct line length to decode_transfer_encoding()

Matthew Ogilvie (1):
  Documentation cvs: Clarify when a bare repository is needed

Mikael Magnusson (1):
  Fix grammar in git-rev-parse(1).

Nikolaus Schulz (1):
  Documentation: be precise about which date --pretty uses


* The 'master' branch has these since the last announcement
  in addition to the above.

Abhijit Menon-Sen (2):
  git-gui: Move on to the next filename after staging/unstaging a change
  git-gui: Don't select the wrong file if the last listed file is staged.

Daniel Barkalow (1):
  Only use GIT_CONFIG in "git config", not other programs

David Reiss (4):
  Implement normalize_absolute_path
  Fold test-absolute-path into test-path-utils
  Add support for GIT_CEILING_DIRECTORIES
  Eliminate an unnecessary chdir("..")

Dmitry Potapov (1):
  completion.bash: add 'skip' and 'run' to git-bisect

Jakub Narebski (1):
  gitweb: Describe projects_index format in more detail

Johannes Schindelin (3):
  Add another fast-import example, this time for .zip files
  git daemon: avoid calling syslog() from a signal handler
  run_command(): respect GIT_TRACE

Johannes Sixt (1):
  git-gui: Implement "Stage/Unstage Line"

Junio C Hamano (6):
  rerere: rerere_created_at() and has_resolution() abstraction
  git-rerere: detect unparsable conflicts
  rerere: remove dubious "tail_optimization"
  t4200: fix rerere test
  rerere.autoupdate
  Update draft release notes for 1.6.0

Richard Quirk (1):
  git-gui: Fix accidental staged state toggle when clicking top pixel row

^ permalink raw reply

* Re: [PATCH] fix "git-submodule add a/b/c/repository"
From: Junio C Hamano @ 2008-07-08  2:42 UTC (permalink / raw)
  To: Mark Levedahl; +Cc: Sylvain Joyeux, Lars Hjemli, Ping Yin, git
In-Reply-To: <4872CF86.5050702@gmail.com>

Mark Levedahl <mlevedahl@gmail.com> writes:

> Sylvain Joyeux wrote:
>>
>> Redo the prep work, the clone and now
>>
>> git submodule add dir0/dir1/init
>>
>> (i.e. don't expect dir0/dir1/init to be the clone of ./init, that was just a
>> shortcut for the test. Expect it to be a clone of "something, somewhere")
>>
>>
> Per the man-page,
>    git submodule [--quiet] add [-b branch] [--] <repository> [<path>]
>
> which means, that the *repository* url is mandatory, the path is
> optional. What you specifically asked git-submodule to do was to
> *clone* from dir0/dir1/init, and because you gave no path to put the
> submodule in, git-submodule deduced the name as "init", and cloned to
> there.

I'd like to hear clarifications on two counts, please?

 (1) If Sylvain wanted to have that appear at dir0/dir1/init not init,
     would it have been sufficient to give that path twice (once for
     <repository> and another for <path> parameter) to make things work as
     expected?

 (2) Is it generally considered a sane use case to specify an existing
     repository inside the working tree of a superproject as a submodule
     using "git submodule add" like Sylvain's example did?

     I would have understood if the command were "git add dir0/dir1/init",
     but I have this vague recolleciton that "git submodule add" is about
     telling our repository about a submodule that comes from _outside_.

^ permalink raw reply

* [PATCH] install-doc-quick - use git --exec-path to find git-sh-setup
From: Mark Levedahl @ 2008-07-08  2:37 UTC (permalink / raw)
  To: git; +Cc: Mark Levedahl

This is needed as git-sh-setup is no longer in the path.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 Documentation/install-doc-quick.sh |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh
index 5433cf8..35f4408 100755
--- a/Documentation/install-doc-quick.sh
+++ b/Documentation/install-doc-quick.sh
@@ -6,7 +6,7 @@ head="$1"
 mandir="$2"
 SUBDIRECTORY_OK=t
 USAGE='<refname> <target directory>'
-. git-sh-setup
+. "$(git --exec-path)"/git-sh-setup
 cd_to_toplevel
 
 test -z "$mandir" && usage
-- 
1.5.6.2.271.g73ad8

^ permalink raw reply related

* [PATCH] git-submodule - register module url if adding in place
From: Mark Levedahl @ 2008-07-08  2:37 UTC (permalink / raw)
  To: git; +Cc: Mark Levedahl

When adding a new submodule in place, meaning the user created the
submodule as a git repo in the tree first, we don't go through
git submodule init to register the module. Thus, the url is
not stored in .git/config, and no git operation will ever do so. In
this case, assume the url the user supplies to git add is the one
that should be registered, and do so.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-submodule.sh |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/git-submodule.sh b/git-submodule.sh
index 099a7d7..7525922 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -173,6 +173,16 @@ cmd_add()
 		else
 			die "'$path' already exists and is not a valid git repo"
 		fi
+
+		case "$repo" in
+		./*|../*)
+			url=$(resolve_relative_url "$repo") || exit
+		    ;;
+		*)
+			url="$repo"
+			;;
+		esac
+		git config submodule."$path".url "$url"
 	else
 		case "$repo" in
 		./*|../*)
-- 
1.5.6.2.271.g73ad8

^ permalink raw reply related

* [PATCH] git-submodule - Fix bugs in adding an existing repo as a module
From: Mark Levedahl @ 2008-07-08  2:36 UTC (permalink / raw)
  To: git; +Cc: Mark Levedahl

git-submodule add would trip if path to the submodule included a space,
or if its .git was a gitdir: link to a GIT_DIR kept elsewhere. Fix both.

Signed-off-by: Mark Levedahl <mlevedahl@gmail.com>
---
 git-submodule.sh |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/git-submodule.sh b/git-submodule.sh
index 3eb78cc..099a7d7 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -167,8 +167,7 @@ cmd_add()
 	# perhaps the path exists and is already a git repo, else clone it
 	if test -e "$path"
 	then
-		if test -d "$path/.git" &&
-		test "$(unset GIT_DIR; cd $path; git rev-parse --git-dir)" = ".git"
+		if test -d "$path"/.git -o -f "$path"/.git
 		then
 			echo "Adding existing repo at '$path' to the index"
 		else
-- 
1.5.6.2.271.g73ad8

^ permalink raw reply related

* Re: [PATCH] fix "git-submodule add a/b/c/repository"
From: Mark Levedahl @ 2008-07-08  2:23 UTC (permalink / raw)
  To: Sylvain Joyeux; +Cc: Junio C Hamano, Lars Hjemli, Ping Yin, git
In-Reply-To: <20080707063424.GB5506@jhaampe.org>

Sylvain Joyeux wrote:
>
> Redo the prep work, the clone and now
>
> git submodule add dir0/dir1/init
>
> (i.e. don't expect dir0/dir1/init to be the clone of ./init, that was just a
> shortcut for the test. Expect it to be a clone of "something, somewhere")
>
>   
Per the man-page,
    git submodule [--quiet] add [-b branch] [--] <repository> [<path>]

which means, that the *repository* url is mandatory, the path is 
optional. What you specifically asked git-submodule to do was to *clone* 
from dir0/dir1/init, and because you gave no path to put the submodule 
in, git-submodule deduced the name as "init", and cloned to there.

Mark

^ permalink raw reply

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Stephan Beyer @ 2008-07-08  1:47 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Jakub Narebski, git, Sam Vilain, Joshua Roys, Sverre Rabbelier,
	Sverre Rabbelier, David Symonds, Lea Wiemann, John Hawley,
	Marek Zawirski, Shawn O. Pearce, Miklos Vajna,
	Johannes Schindelin, Christian Couder, Daniel Barkalow
In-Reply-To: <7vmyktxjlq.fsf@gitster.siamese.dyndns.org>

Hi,

Junio C Hamano <gitster@pobox.com> wrote:
> Stephan Beyer <s-beyer@gmx.net> writes:
> > (Well, I think the
> >    http://thread.gmane.org/gmane.comp.version-control.git/86985
> > thread died in the last days, but I hope some responses will come and
> > also that Junio's patch for cherry-picking root commits will be
> > included. I try to be patient...) ;-)
> 
> Please don't be patient but actively re-review what you sent out.
> 
> I _really_ wanted to merge the basic bits and rewrite of "am" at least to
> pu tonight, but I had to drop them after noticing that it does not seem to
> handle --rebasing at all (it parses to set $rebasing but after that where
> does that bit go?

Yes, you are right that am --rebasing is a no-op.
That option was a little mystery to me, because it seemed to do nothing
special, but I'll check again (bash-completion etc) and do appropriate
changes.

> About the "rewrite rebase to use sequencer" bits, because we've dropped
> the older rebase-i change, I do not want your series to depend on it.

Yes, I made the mistake that I started developing on "next" and I did
not expect that js/rebase-i-sequencer could be dropped. (I somehow
expected the opposite, that it would be migrated to master some day.)

Then I wanted to make it work on master, and thought this is simply
done by removing the "additional features" (-f and -p option using extended
todo list), but then I noticed that master also has a -p which works
entirely different and I wanted to see that stuff on the list before
July 1st and I had no idea what the status of js/rebase-i-sequencer is.
So I just tried and sent that last patch to the list in that way.

But now I have a clear statement.

So I'm going to send a new patchset (also with an EXAMPLES section in 
the docs) to the list in a few days.

Big thanks for the feedback,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply

* Re: [PATCH] Build in merge
From: Miklos Vajna @ 2008-07-08  1:41 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7vskulxk0o.fsf@gitster.siamese.dyndns.org>

[-- Attachment #1: Type: text/plain, Size: 1232 bytes --]

On Mon, Jul 07, 2008 at 06:05:43PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> > 1) A project has code in a repo, some documentation and po files.
> >
> > The first two can be merged with builtin strategies, the later probably
> > needs a custom merge driver.
> 
> Per-path merge is probably better handled with custom ll-merge driver
> anyway.  See gitattributes(5).

Ah, thanks. I did not know ll-merge supports custom drivers as well.

> It wasn't *me* ;-) who wanted to add these "flags".
> 
> I think it does not matter what "my-strategy" does unless "-s my-strategy"
> (or pull.twohead) is explicitly given by the user, and at that time,
> DEFAULT_* options should not matter.  It probably is Ok to allow fast
> forward and trivial merges to them.  We'll see.

OK, so at first round I think we could avoid flags for custom
strategies.

For the error message, I think the output could be something like git
help --all, which splits commands based on being in
GIT_EXEC_PATH or somewhere else in PATH.

Also, currently (the shell version and the C one as well) we silently
ignore the config setting if it's set to semething invalid. What about
changing that to a similar error message as well?

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Josef 'Jeff' Sipek @ 2008-07-08  1:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Theodore Ts'o, git
In-Reply-To: <7viqvhxjhc.fsf@gitster.siamese.dyndns.org>

On Mon, Jul 07, 2008 at 06:17:19PM -0700, Junio C Hamano wrote:
> "Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:
> 
> > A fresh clone (from master.kernel.org over ssh) gets me:
> >
> > $ git describe
> > v1.5.6.2-247-g9237122
> > $ git --version
> > git version 1.5.6.GIT
> > $ git --exec-path
> > /home/jeffpc/git-bin/bin
> >
> > You seem to have a different HEAD for the master branch than I do.
> 
> Oh, my private 'master' almost always is ahead of the public one after
> work.
> 
> But I do not have any new changes around this area lately.  Promise ;-).

Heh.

Alright, mystery solved. I had a GIT_EXEC_PATH set. Removing it from my
bash_profile makes everything work just fine.

Sorry for the noise.

Jeff.

-- 
Penguin : Linux version 2.6.25.4 on an i386 machine (6135.77 BogoMips).

^ permalink raw reply

* [PATCH] Build in merge
From: Miklos Vajna @ 2008-07-08  1:18 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7v1w25yz5o.fsf@gitster.siamese.dyndns.org>

Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
---

On Mon, Jul 07, 2008 at 05:53:23PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> Here is what I meant to suggest; sorry for confusing you with an
> initial
> typo of having only one pointer in front of use_strategies "array of
> pointers".  Applies on top of your previous round.

Funny enough, I just did almost the same before reading your mail,
except that I did not do the suggested strategy_lookup() ->
get_strategy() rename and such.

Here is a squashed patch. (Previous round + your patch.)

 Makefile                                      |    2 +-
 builtin-merge.c                               | 1153 +++++++++++++++++++++++++
 builtin.h                                     |    1 +
 git-merge.sh => contrib/examples/git-merge.sh |    0
 git.c                                         |    1 +
 t/t7602-merge-octopus-many.sh                 |    2 +-
 6 files changed, 1157 insertions(+), 2 deletions(-)
 create mode 100644 builtin-merge.c
 rename git-merge.sh => contrib/examples/git-merge.sh (100%)

diff --git a/Makefile b/Makefile
index bf77292..fbc53e9 100644
--- a/Makefile
+++ b/Makefile
@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
-SCRIPT_SH += git-merge.sh
 SCRIPT_SH += git-merge-stupid.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-parse-remote.sh
@@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
 BUILTIN_OBJS += builtin-ls-tree.o
 BUILTIN_OBJS += builtin-mailinfo.o
 BUILTIN_OBJS += builtin-mailsplit.o
+BUILTIN_OBJS += builtin-merge.o
 BUILTIN_OBJS += builtin-merge-base.o
 BUILTIN_OBJS += builtin-merge-file.o
 BUILTIN_OBJS += builtin-merge-ours.o
diff --git a/builtin-merge.c b/builtin-merge.c
new file mode 100644
index 0000000..d6bd144
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1153 @@
+/*
+ * Builtin "git merge"
+ *
+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>
+ *
+ * Based on git-merge.sh by Junio C Hamano.
+ */
+
+#include "cache.h"
+#include "parse-options.h"
+#include "builtin.h"
+#include "run-command.h"
+#include "diff.h"
+#include "refs.h"
+#include "commit.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "utf8.h"
+#include "log-tree.h"
+#include "color.h"
+
+#define DEFAULT_TWOHEAD (1<<0)
+#define DEFAULT_OCTOPUS (1<<1)
+#define NO_FAST_FORWARD (1<<2)
+#define NO_TRIVIAL      (1<<3)
+
+struct strategy {
+	const char *name;
+	unsigned attr;
+};
+
+static const char * const builtin_merge_usage[] = {
+	"git-merge [options] <remote>...",
+	"git-merge [options] <msg> HEAD <remote>",
+	NULL
+};
+
+static int show_diffstat = 1, option_log, squash;
+static int option_commit = 1, allow_fast_forward = 1;
+static int allow_trivial = 1, have_message;
+static struct strbuf merge_msg;
+static struct commit_list *remoteheads;
+static unsigned char head[20], stash[20];
+static struct strategy **use_strategies;
+static size_t use_strategies_nr, use_strategies_alloc;
+static const char *branch;
+
+static struct strategy all_strategy[] = {
+	{ "recur",      NO_TRIVIAL },
+	{ "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
+	{ "octopus",    DEFAULT_OCTOPUS },
+	{ "resolve",    0 },
+	{ "stupid",     0 },
+	{ "ours",       NO_FAST_FORWARD | NO_TRIVIAL },
+	{ "subtree",    NO_FAST_FORWARD | NO_TRIVIAL },
+};
+
+static const char *pull_twohead, *pull_octopus;
+
+static int option_parse_message(const struct option *opt,
+	const char *arg, int unset)
+{
+	struct strbuf *buf = opt->value;
+
+	if (unset)
+		strbuf_setlen(buf, 0);
+	else {
+		strbuf_addf(buf, "%s\n\n", arg);
+		have_message = 1;
+	}
+	return 0;
+}
+
+static struct strategy *get_strategy(const char *name)
+{
+	int i;
+
+	if (!name)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (!strcmp(name, all_strategy[i].name))
+			return &all_strategy[i];
+	return NULL;
+}
+
+static void append_strategy(struct strategy *s)
+{
+	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
+	use_strategies[use_strategies_nr++] = s;
+}
+
+static int option_parse_strategy(const struct option *opt,
+				 const char *name, int unset)
+{
+	int i;
+	struct strategy *s;
+
+	if (unset)
+		return 0;
+
+	s = get_strategy(name);
+
+	if (s)
+		append_strategy(s);
+	else {
+		struct strbuf err;
+		strbuf_init(&err, 0);
+		for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+			strbuf_addf(&err, " %s", all_strategy[i].name);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
+		exit(1);
+	}
+	return 0;
+}
+
+static int option_parse_n(const struct option *opt,
+		const char *arg, int unset)
+{
+	show_diffstat = unset;
+	return 0;
+}
+
+static struct option builtin_merge_options[] = {
+	{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+		"do not show a diffstat at the end of the merge",
+		PARSE_OPT_NOARG, option_parse_n },
+	OPT_BOOLEAN(0, "stat", &show_diffstat,
+		"show a diffstat at the end of the merge"),
+	OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
+	OPT_BOOLEAN(0, "log", &option_log,
+		"add list of one-line log to merge commit message"),
+	OPT_BOOLEAN(0, "squash", &squash,
+		"create a single commit instead of doing a merge"),
+	OPT_BOOLEAN(0, "commit", &option_commit,
+		"perform a commit if the merge succeeds (default)"),
+	OPT_BOOLEAN(0, "ff", &allow_fast_forward,
+		"allow fast forward (default)"),
+	OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
+		"merge strategy to use", option_parse_strategy),
+	OPT_CALLBACK('m', "message", &merge_msg, "message",
+		"message to be used for the merge commit (if any)",
+		option_parse_message),
+	OPT_END()
+};
+
+/* Cleans up metadata that is uninteresting after a succeeded merge. */
+static void drop_save(void)
+{
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("MERGE_MSG"));
+}
+
+static void save_state(void)
+{
+	int len;
+	struct child_process cp;
+	struct strbuf buffer = STRBUF_INIT;
+	const char *argv[] = {"stash", "create", NULL};
+
+	memset(&cp, 0, sizeof(cp));
+	cp.argv = argv;
+	cp.out = -1;
+	cp.git_cmd = 1;
+
+	if (start_command(&cp))
+		die("could not run stash.");
+	len = strbuf_read(&buffer, cp.out, 1024);
+	close(cp.out);
+
+	if (finish_command(&cp) || len < 0)
+		die("stash failed");
+	else if (!len)
+		return;
+	strbuf_setlen(&buffer, buffer.len-1);
+	if (get_sha1(buffer.buf, stash))
+		die("not a valid object: %s", buffer.buf);
+}
+
+static void reset_hard(unsigned const char *sha1, int verbose)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	if (verbose)
+		args[i++] = "-v";
+	args[i++] = "--reset";
+	args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	if (run_command_v_opt(args, RUN_GIT_CMD))
+		die("read-tree failed");
+}
+
+static void restore_state(void)
+{
+	struct strbuf sb;
+	const char *args[] = { "stash", "apply", NULL, NULL };
+
+	if (is_null_sha1(stash))
+		return;
+
+	reset_hard(head, 1);
+
+	strbuf_init(&sb, 0);
+	args[2] = sha1_to_hex(stash);
+
+	/*
+	 * It is OK to ignore error here, for example when there was
+	 * nothing to restore.
+	 */
+	run_command_v_opt(args, RUN_GIT_CMD);
+
+	strbuf_release(&sb);
+	refresh_cache(REFRESH_QUIET);
+}
+
+/* This is called when no merge was necessary. */
+static void finish_up_to_date(const char *msg)
+{
+	printf("%s%s\n", squash ? " (nothing to squash)" : "", msg);
+	drop_save();
+}
+
+static void squash_message(void)
+{
+	struct rev_info rev;
+	struct commit *commit;
+	struct strbuf out;
+	struct commit_list *j;
+	int fd;
+
+	printf("Squash commit -- not updating HEAD\n");
+	fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
+	if (fd < 0)
+		die("Could not write to %s", git_path("SQUASH_MSG"));
+
+	init_revisions(&rev, NULL);
+	rev.ignore_merges = 1;
+	rev.commit_format = CMIT_FMT_MEDIUM;
+
+	commit = lookup_commit(head);
+	commit->object.flags |= UNINTERESTING;
+	add_pending_object(&rev, &commit->object, NULL);
+
+	for (j = remoteheads; j; j = j->next)
+		add_pending_object(&rev, &j->item->object, NULL);
+
+	setup_revisions(0, NULL, &rev, NULL);
+	if (prepare_revision_walk(&rev))
+		die("revision walk setup failed");
+
+	strbuf_init(&out, 0);
+	strbuf_addstr(&out, "Squashed commit of the following:\n");
+	while ((commit = get_revision(&rev)) != NULL) {
+		strbuf_addch(&out, '\n');
+		strbuf_addf(&out, "commit %s\n",
+			sha1_to_hex(commit->object.sha1));
+		pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,
+			NULL, NULL, rev.date_mode, 0);
+	}
+	write(fd, out.buf, out.len);
+	close(fd);
+	strbuf_release(&out);
+}
+
+static int run_hook(const char *name)
+{
+	struct child_process hook;
+	const char *argv[3], *env[2];
+	char index[PATH_MAX];
+
+	argv[0] = git_path("hooks/%s", name);
+	if (access(argv[0], X_OK) < 0)
+		return 0;
+
+	snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
+	env[0] = index;
+	env[1] = NULL;
+
+	if (squash)
+		argv[1] = "1";
+	else
+		argv[1] = "0";
+	argv[2] = NULL;
+
+	memset(&hook, 0, sizeof(hook));
+	hook.argv = argv;
+	hook.no_stdin = 1;
+	hook.stdout_to_stderr = 1;
+	hook.env = env;
+
+	return run_command(&hook);
+}
+
+static void finish(const unsigned char *new_head, const char *msg)
+{
+	struct strbuf reflog_message;
+
+	strbuf_init(&reflog_message, 0);
+	if (!msg)
+		strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
+	else {
+		printf("%s\n", msg);
+		strbuf_addf(&reflog_message, "%s: %s",
+			getenv("GIT_REFLOG_ACTION"), msg);
+	}
+	if (squash) {
+		squash_message();
+	} else {
+		if (!merge_msg.len)
+			printf("No merge message -- not updating HEAD\n");
+		else {
+			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
+			update_ref(reflog_message.buf, "HEAD",
+				new_head, head, 0,
+				DIE_ON_ERR);
+			/*
+			 * We ignore errors in 'gc --auto', since the
+			 * user should see them.
+			 */
+			run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+		}
+	}
+	if (new_head && show_diffstat) {
+		struct diff_options opts;
+		diff_setup(&opts);
+		opts.output_format |=
+			DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
+		opts.detect_rename = DIFF_DETECT_RENAME;
+		if (diff_use_color_default > 0)
+			DIFF_OPT_SET(&opts, COLOR_DIFF);
+		if (diff_setup_done(&opts) < 0)
+			die("diff_setup_done failed");
+		diff_tree_sha1(head, new_head, "", &opts);
+		diffcore_std(&opts);
+		diff_flush(&opts);
+	}
+
+	/* Run a post-merge hook */
+	run_hook("post-merge");
+
+	strbuf_release(&reflog_message);
+}
+
+/* Get the name for the merge commit's message. */
+static void merge_name(const char *remote, struct strbuf *msg)
+{
+	struct object *remote_head;
+	unsigned char branch_head[20], buf_sha[20];
+	struct strbuf buf;
+	const char *ptr;
+	int len, early;
+
+	memset(branch_head, 0, sizeof(branch_head));
+	remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
+	if (!remote_head)
+		die("'%s' does not point to a commit", remote);
+
+	strbuf_init(&buf, 0);
+	strbuf_addstr(&buf, "refs/heads/");
+	strbuf_addstr(&buf, remote);
+	resolve_ref(buf.buf, branch_head, 0, 0);
+
+	if (!hashcmp(remote_head->sha1, branch_head)) {
+		strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+			sha1_to_hex(branch_head), remote);
+		return;
+	}
+
+	/* See if remote matches <name>^^^.. or <name>~<number> */
+	for (len = 0, ptr = remote + strlen(remote);
+	     remote < ptr && ptr[-1] == '^';
+	     ptr--)
+		len++;
+	if (len)
+		early = 1;
+	else {
+		early = 0;
+		ptr = strrchr(remote, '~');
+		if (ptr) {
+			int seen_nonzero = 0;
+
+			len++; /* count ~ */
+			while (*++ptr && isdigit(*ptr)) {
+				seen_nonzero |= (*ptr != '0');
+				len++;
+			}
+			if (*ptr)
+				len = 0; /* not ...~<number> */
+			else if (seen_nonzero)
+				early = 1;
+			else if (len == 1)
+				early = 1; /* "name~" is "name~1"! */
+		}
+	}
+	if (len) {
+		struct strbuf truname = STRBUF_INIT;
+		strbuf_addstr(&truname, "refs/heads/");
+		strbuf_addstr(&truname, remote);
+		strbuf_setlen(&truname, len+11);
+		if (resolve_ref(truname.buf, buf_sha, 0, 0)) {
+			strbuf_addf(msg,
+				    "%s\t\tbranch '%s'%s of .\n",
+				    sha1_to_hex(remote_head->sha1),
+				    truname.buf,
+				    (early ? " (early part)" : ""));
+			return;
+		}
+	}
+
+	if (!strcmp(remote, "FETCH_HEAD") &&
+			!access(git_path("FETCH_HEAD"), R_OK)) {
+		FILE *fp;
+		struct strbuf line;
+		char *ptr;
+
+		strbuf_init(&line, 0);
+		fp = fopen(git_path("FETCH_HEAD"), "r");
+		if (!fp)
+			die("could not open %s for reading: %s",
+				git_path("FETCH_HEAD"), strerror(errno));
+		strbuf_getline(&line, fp, '\n');
+		fclose(fp);
+		ptr = strstr(line.buf, "\tnot-for-merge\t");
+		if (ptr)
+			strbuf_remove(&line, ptr-line.buf+1, 13);
+		strbuf_addbuf(msg, &line);
+		strbuf_release(&line);
+		return;
+	}
+	strbuf_addf(msg, "%s\t\tcommit '%s'\n",
+		sha1_to_hex(remote_head->sha1), remote);
+}
+
+int git_merge_config(const char *k, const char *v, void *cb)
+{
+	if (branch && !prefixcmp(k, "branch.") &&
+		!prefixcmp(k + 7, branch) &&
+		!strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+		const char **argv;
+		int argc;
+		char *buf;
+
+		buf = xstrdup(v);
+		argc = split_cmdline(buf, &argv);
+		argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+		memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+		argc++;
+		parse_options(argc, argv, builtin_merge_options,
+			      builtin_merge_usage, 0);
+		free(buf);
+	}
+
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+		show_diffstat = git_config_bool(k, v);
+	else if (!strcmp(k, "pull.twohead"))
+		return git_config_string(&pull_twohead, k, v);
+	else if (!strcmp(k, "pull.octopus"))
+		return git_config_string(&pull_octopus, k, v);
+	return git_diff_ui_config(k, v, cb);
+}
+
+static int read_tree_trivial(unsigned char *common, unsigned char *head,
+	unsigned char *one)
+{
+	int i, nr_trees = 0;
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct tree_desc t[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = 2;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.trivial_merges_only = 1;
+	opts.merge = 1;
+	trees[nr_trees] = parse_tree_indirect(common);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(one);
+	if (!trees[nr_trees++])
+		return -1;
+	opts.fn = threeway_merge;
+	cache_tree_free(&active_cache_tree);
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	return 0;
+}
+
+static void write_tree_trivial(unsigned char *sha1)
+{
+	if (write_cache_as_tree(sha1, 0, NULL))
+		die("git write-tree failed to write a tree");
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+	const char *head_arg)
+{
+	const char **args;
+	int i = 0, ret;
+	struct commit_list *j;
+	struct strbuf buf;
+
+	args = xmalloc((4 + commit_list_count(common) +
+			commit_list_count(remoteheads)) * sizeof(char *));
+	strbuf_init(&buf, 0);
+	strbuf_addf(&buf, "merge-%s", strategy);
+	args[i++] = buf.buf;
+	for (j = common; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i++] = "--";
+	args[i++] = head_arg;
+	for (j = remoteheads; j; j = j->next)
+		args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+	args[i] = NULL;
+	ret = run_command_v_opt(args, RUN_GIT_CMD);
+	strbuf_release(&buf);
+	i = 1;
+	for (j = common; j; j = j->next)
+		free((void *)args[i++]);
+	i += 2;
+	for (j = remoteheads; j; j = j->next)
+		free((void *)args[i++]);
+	free(args);
+	return -ret;
+}
+
+static void count_diff_files(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int *count = data;
+
+	(*count) += q->nr;
+}
+
+static int count_unmerged_entries(void)
+{
+	const struct index_state *state = &the_index;
+	int i, ret = 0;
+
+	for (i = 0; i < state->cache_nr; i++)
+		if (ce_stage(state->cache[i]))
+			ret++;
+
+	return ret;
+}
+
+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
+{
+	struct tree *trees[MAX_UNPACK_TREES];
+	struct unpack_trees_options opts;
+	struct tree_desc t[MAX_UNPACK_TREES];
+	int i, fd, nr_trees = 0;
+	struct dir_struct dir;
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	if (read_cache_unmerged())
+		die("you need to resolve your current index first");
+
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&trees, 0, sizeof(trees));
+	memset(&opts, 0, sizeof(opts));
+	memset(&t, 0, sizeof(t));
+	dir.show_ignored = 1;
+	dir.exclude_per_dir = ".gitignore";
+	opts.dir = &dir;
+
+	opts.head_idx = 1;
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	opts.update = 1;
+	opts.verbose_update = 1;
+	opts.merge = 1;
+	opts.fn = twoway_merge;
+
+	trees[nr_trees] = parse_tree_indirect(head);
+	if (!trees[nr_trees++])
+		return -1;
+	trees[nr_trees] = parse_tree_indirect(remote);
+	if (!trees[nr_trees++])
+		return -1;
+	for (i = 0; i < nr_trees; i++) {
+		parse_tree(trees[i]);
+		init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
+	}
+	if (unpack_trees(nr_trees, t, &opts))
+		return -1;
+	if (write_cache(fd, active_cache, active_nr) ||
+		commit_locked_index(lock_file))
+		die("unable to write new index file");
+	return 0;
+}
+
+static void split_merge_strategies(const char *string, struct strategy **list,
+		int *nr, int *alloc)
+{
+	char *p, *q, *buf;
+
+	if (!string)
+		return;
+
+	buf = xstrdup(string);
+	q = buf;
+	for (;;) {
+		p = strchr(q, ' ');
+		if (!p) {
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			free(buf);
+			return;
+		} else {
+			*p = '\0';
+			ALLOC_GROW(*list, *nr + 1, *alloc);
+			(*list)[(*nr)++].name = xstrdup(q);
+			q = ++p;
+		}
+	}
+}
+
+static void add_strategies(const char *string, unsigned attr)
+{
+	struct strategy *list = NULL;
+	int list_alloc = 0, list_nr = 0, i;
+
+	memset(&list, 0, sizeof(list));
+	split_merge_strategies(string, &list, &list_nr, &list_alloc);
+	if (list != NULL) {
+		for (i = 0; i < list_nr; i++) {
+			struct strategy *s;
+
+			s = get_strategy(list[i].name);
+			if (s)
+				append_strategy(s);
+		}
+		return;
+	}
+	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+		if (all_strategy[i].attr & attr)
+			append_strategy(&all_strategy[i]);
+
+}
+
+static int merge_trivial(void)
+{
+	unsigned char result_tree[20], result_commit[20];
+	struct commit_list parent;
+
+	write_tree_trivial(result_tree);
+	printf("Wonderful.\n");
+	parent.item = remoteheads->item;
+	parent.next = NULL;
+	commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+	finish(result_commit, "In-index merge");
+	drop_save();
+	return 0;
+}
+
+static int finish_automerge(struct commit_list *common,
+	unsigned char *result_tree,
+	const char *wt_strategy)
+{
+	struct commit_list *parents = NULL, *j;
+	struct strbuf buf = STRBUF_INIT;
+	unsigned char result_commit[20];
+
+	free_commit_list(common);
+	if (allow_fast_forward) {
+		parents = remoteheads;
+		commit_list_insert(lookup_commit(head), &parents);
+		parents = reduce_heads(parents);
+	} else {
+		struct commit_list **pptr = &parents;
+
+		pptr = &commit_list_insert(lookup_commit(head),
+				pptr)->next;
+		for (j = remoteheads; j; j = j->next)
+			pptr = &commit_list_insert(j->item, pptr)->next;
+	}
+	free_commit_list(remoteheads);
+	strbuf_addch(&merge_msg, '\n');
+	commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+	strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
+	finish(result_commit, buf.buf);
+	strbuf_release(&buf);
+	drop_save();
+	return 0;
+}
+
+static int suggest_conflicts(void)
+{
+	FILE *fp;
+	int pos;
+
+	fp = fopen(git_path("MERGE_MSG"), "a");
+	if (!fp)
+		die("Could open %s for writing", git_path("MERGE_MSG"));
+	fprintf(fp, "\nConflicts:\n");
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+
+		if (ce_stage(ce)) {
+			fprintf(fp, "\t%s\n", ce->name);
+			while (pos + 1 < active_nr &&
+					!strcmp(ce->name,
+						active_cache[pos + 1]->name))
+				pos++;
+		}
+	}
+	fclose(fp);
+	rerere();
+	printf("Automatic merge failed; "
+			"fix conflicts and then commit the result.\n");
+	return 1;
+}
+
+static struct commit *is_old_style_invocation(int argc, const char **argv)
+{
+	struct commit *second_token = NULL;
+	if (argc > 1) {
+		unsigned char second_sha1[20];
+
+		if (get_sha1(argv[1], second_sha1))
+			return NULL;
+		second_token = lookup_commit_reference_gently(second_sha1, 0);
+		if (!second_token)
+			die("'%s' is not a commit", argv[1]);
+		if (hashcmp(second_token->object.sha1, head))
+			return NULL;
+	}
+	return second_token;
+}
+
+static int evaluate_result(void)
+{
+	int cnt = 0;
+	struct rev_info rev;
+
+	if (read_cache() < 0)
+		die("failed to read the cache");
+
+	/* Check how many files differ. */
+	init_revisions(&rev, "");
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.diffopt.output_format |=
+		DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = count_diff_files;
+	rev.diffopt.format_callback_data = &cnt;
+	run_diff_files(&rev, 0);
+
+	/*
+	 * Check how many unmerged entries are
+	 * there.
+	 */
+	cnt += count_unmerged_entries();
+
+	return cnt;
+}
+
+int cmd_merge(int argc, const char **argv, const char *prefix)
+{
+	unsigned char result_tree[20];
+	struct strbuf buf;
+	const char *head_arg;
+	int flag, head_invalid = 0, i;
+	int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
+	struct commit_list *common = NULL;
+	const char *best_strategy = NULL, *wt_strategy = NULL;
+	struct commit_list **remotes = &remoteheads;
+
+	setup_work_tree();
+	if (unmerged_cache())
+		die("You are in the middle of a conflicted merge.");
+
+	/*
+	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
+	 * current branch.
+	 */
+	branch = resolve_ref("HEAD", head, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
+	if (is_null_sha1(head))
+		head_invalid = 1;
+
+	git_config(git_merge_config, NULL);
+
+	/* for color.ui */
+	if (diff_use_color_default == -1)
+		diff_use_color_default = git_use_color_default;
+
+	argc = parse_options(argc, argv, builtin_merge_options,
+			builtin_merge_usage, 0);
+
+	if (squash) {
+		if (!allow_fast_forward)
+			die("You cannot combine --squash with --no-ff.");
+		option_commit = 0;
+	}
+
+	if (!argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	/*
+	 * This could be traditional "merge <msg> HEAD <commit>..."  and
+	 * the way we can tell it is to see if the second token is HEAD,
+	 * but some people might have misused the interface and used a
+	 * committish that is the same as HEAD there instead.
+	 * Traditional format never would have "-m" so it is an
+	 * additional safety measure to check for it.
+	 */
+	strbuf_init(&buf, 0);
+
+	if (!have_message && is_old_style_invocation(argc, argv)) {
+		strbuf_addstr(&merge_msg, argv[0]);
+		head_arg = argv[1];
+		argv += 2;
+		argc -= 2;
+	} else if (head_invalid) {
+		struct object *remote_head;
+		/*
+		 * If the merged head is a valid one there is no reason
+		 * to forbid "git merge" into a branch yet to be born.
+		 * We do the same for "git pull".
+		 */
+		if (argc != 1)
+			die("Can merge only exactly one commit into "
+				"empty head");
+		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
+		if (!remote_head)
+			die("%s - not something we can merge", argv[0]);
+		update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
+				DIE_ON_ERR);
+		reset_hard(remote_head->sha1, 0);
+		return 0;
+	} else {
+		struct strbuf msg;
+
+		/* We are invoked directly as the first-class UI. */
+		head_arg = "HEAD";
+
+		/*
+		 * All the rest are the commits being merged;
+		 * prepare the standard merge summary message to
+		 * be appended to the given message.  If remote
+		 * is invalid we will die later in the common
+		 * codepath so we discard the error in this
+		 * loop.
+		 */
+		strbuf_init(&msg, 0);
+		for (i = 0; i < argc; i++)
+			merge_name(argv[i], &msg);
+		fmt_merge_msg(option_log, &msg, &merge_msg);
+		if (merge_msg.len)
+			strbuf_setlen(&merge_msg, merge_msg.len-1);
+	}
+
+	if (head_invalid || !argc)
+		usage_with_options(builtin_merge_usage,
+			builtin_merge_options);
+
+	strbuf_addstr(&buf, "merge");
+	for (i = 0; i < argc; i++)
+		strbuf_addf(&buf, " %s", argv[i]);
+	setenv("GIT_REFLOG_ACTION", buf.buf, 0);
+	strbuf_reset(&buf);
+
+	for (i = 0; i < argc; i++) {
+		struct object *o;
+
+		o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);
+		if (!o)
+			die("%s - not something we can merge", argv[i]);
+		remotes = &commit_list_insert(lookup_commit(o->sha1),
+			remotes)->next;
+
+		strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
+		setenv(buf.buf, argv[i], 1);
+		strbuf_reset(&buf);
+	}
+
+	if (!use_strategies) {
+		if (!remoteheads->next)
+			add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+		else
+			add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		if (use_strategies[i]->attr & NO_FAST_FORWARD)
+			allow_fast_forward = 0;
+		if (use_strategies[i]->attr & NO_TRIVIAL)
+			allow_trivial = 0;
+	}
+
+	if (!remoteheads->next)
+		common = get_merge_bases(lookup_commit(head),
+				remoteheads->item, 1);
+	else {
+		struct commit_list *list = remoteheads;
+		commit_list_insert(lookup_commit(head), &list);
+		common = get_octopus_merge_bases(list);
+		free(list);
+	}
+
+	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
+		DIE_ON_ERR);
+
+	if (!common)
+		; /* No common ancestors found. We need a real merge. */
+	else if (!remoteheads->next && !common->next &&
+			common->item == remoteheads->item) {
+		/*
+		 * If head can reach all the merge then we are up to date.
+		 * but first the most common case of merging one remote.
+		 */
+		finish_up_to_date("Already up-to-date.");
+		return 0;
+	} else if (allow_fast_forward && !remoteheads->next &&
+			!common->next &&
+			!hashcmp(common->item->object.sha1, head)) {
+		/* Again the most common case of merging one remote. */
+		struct strbuf msg;
+		struct object *o;
+		char hex[41];
+
+		strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+
+		printf("Updating %s..%s\n",
+			hex,
+			find_unique_abbrev(remoteheads->item->object.sha1,
+			DEFAULT_ABBREV));
+		refresh_cache(REFRESH_QUIET);
+		strbuf_init(&msg, 0);
+		strbuf_addstr(&msg, "Fast forward");
+		if (have_message)
+			strbuf_addstr(&msg,
+				" (no commit created; -m option ignored)");
+		o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),
+			0, NULL, OBJ_COMMIT);
+		if (!o)
+			return 1;
+
+		if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+			return 1;
+
+		finish(o->sha1, msg.buf);
+		drop_save();
+		return 0;
+	} else if (!remoteheads->next && common->next)
+		;
+		/*
+		 * We are not doing octopus and not fast forward.  Need
+		 * a real merge.
+		 */
+	else if (!remoteheads->next && !common->next && option_commit) {
+		/*
+		 * We are not doing octopus, not fast forward, and have
+		 * only one common.
+		 */
+		refresh_cache(REFRESH_QUIET);
+		if (allow_trivial) {
+			/* See if it is really trivial. */
+			git_committer_info(IDENT_ERROR_ON_NO_NAME);
+			printf("Trying really trivial in-index merge...\n");
+			if (!read_tree_trivial(common->item->object.sha1,
+					head, remoteheads->item->object.sha1))
+				return merge_trivial();
+			printf("Nope.\n");
+		}
+	} else {
+		/*
+		 * An octopus.  If we can reach all the remote we are up
+		 * to date.
+		 */
+		int up_to_date = 1;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next) {
+			struct commit_list *common_one;
+
+			/*
+			 * Here we *have* to calculate the individual
+			 * merge_bases again, otherwise "git merge HEAD^
+			 * HEAD^^" would be missed.
+			 */
+			common_one = get_merge_bases(lookup_commit(head),
+				j->item, 1);
+			if (hashcmp(common_one->item->object.sha1,
+				j->item->object.sha1)) {
+				up_to_date = 0;
+				break;
+			}
+		}
+		if (up_to_date) {
+			finish_up_to_date("Already up-to-date. Yeeah!");
+			return 0;
+		}
+	}
+
+	/* We are going to make a new commit. */
+	git_committer_info(IDENT_ERROR_ON_NO_NAME);
+
+	/*
+	 * 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.  The strategies are responsible
+	 * to ensure this.
+	 */
+	if (use_strategies_nr != 1) {
+		/*
+		 * Stash away the local changes so that we can try more
+		 * than one.
+		 */
+		save_state();
+	} else {
+		memcpy(stash, null_sha1, 20);
+	}
+
+	for (i = 0; i < use_strategies_nr; i++) {
+		int ret;
+		if (i) {
+			printf("Rewinding the tree to pristine...\n");
+			restore_state();
+		}
+		if (use_strategies_nr != 1)
+			printf("Trying merge strategy %s...\n",
+				use_strategies[i]->name);
+		/*
+		 * Remember which strategy left the state in the working
+		 * tree.
+		 */
+		wt_strategy = use_strategies[i]->name;
+
+		ret = try_merge_strategy(use_strategies[i]->name,
+			common, head_arg);
+		if (!option_commit && !ret) {
+			merge_was_ok = 1;
+			/*
+			 * This is necessary here just to avoid writing
+			 * the tree, but later we will *not* exit with
+			 * status code 1 because merge_was_ok is set.
+			 */
+			ret = 1;
+		}
+
+		if (ret) {
+			/*
+			 * The backend exits with 1 when conflicts are
+			 * left to be resolved, with 2 when it does not
+			 * handle the given merge at all.
+			 */
+			if (ret == 1) {
+				int cnt = evaluate_result();
+
+				if (best_cnt <= 0 || cnt <= best_cnt) {
+					best_strategy = use_strategies[i]->name;
+					best_cnt = cnt;
+				}
+			}
+			if (merge_was_ok)
+				break;
+			else
+				continue;
+		}
+
+		/* Automerge succeeded. */
+		write_tree_trivial(result_tree);
+		automerge_was_ok = 1;
+		break;
+	}
+
+	/*
+	 * If we have a resulting tree, that means the strategy module
+	 * auto resolved the merge cleanly.
+	 */
+	if (automerge_was_ok)
+		return finish_automerge(common, result_tree, wt_strategy);
+
+	/*
+	 * Pick the result from the best strategy and have the user fix
+	 * it up.
+	 */
+	if (!best_strategy) {
+		restore_state();
+		if (use_strategies_nr > 1)
+			fprintf(stderr,
+				"No merge strategy handled the merge.\n");
+		else
+			fprintf(stderr, "Merge with strategy %s failed.\n",
+				use_strategies[0]->name);
+		return 2;
+	} else if (best_strategy == wt_strategy)
+		; /* We already have its result in the working tree. */
+	else {
+		printf("Rewinding the tree to pristine...\n");
+		restore_state();
+		printf("Using the %s to prepare resolving by hand.\n",
+			best_strategy);
+		try_merge_strategy(best_strategy, common, head_arg);
+	}
+
+	if (squash)
+		finish(NULL, NULL);
+	else {
+		int fd;
+		struct commit_list *j;
+
+		for (j = remoteheads; j; j = j->next)
+			strbuf_addf(&buf, "%s\n",
+				sha1_to_hex(j->item->object.sha1));
+		fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing",
+				git_path("MERGE_HEAD"));
+		if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+			die("Could not write to %s", git_path("MERGE_HEAD"));
+		close(fd);
+		strbuf_addch(&merge_msg, '\n');
+		fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+		if (fd < 0)
+			die("Could open %s for writing", git_path("MERGE_MSG"));
+		if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
+			merge_msg.len)
+			die("Could not write to %s", git_path("MERGE_MSG"));
+		close(fd);
+	}
+
+	if (merge_was_ok) {
+		fprintf(stderr, "Automatic merge went well; "
+			"stopped before committing as requested\n");
+		return 0;
+	} else
+		return suggest_conflicts();
+}
diff --git a/builtin.h b/builtin.h
index 05ee56f..0e605d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -64,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
diff --git a/git-merge.sh b/contrib/examples/git-merge.sh
similarity index 100%
rename from git-merge.sh
rename to contrib/examples/git-merge.sh
diff --git a/git.c b/git.c
index 2fbe96b..770aadd 100644
--- a/git.c
+++ b/git.c
@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
 		{ "merge-file", cmd_merge_file },
 		{ "merge-ours", cmd_merge_ours, RUN_SETUP },
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
index f3a4bb2..fcb8285 100755
--- a/t/t7602-merge-octopus-many.sh
+++ b/t/t7602-merge-octopus-many.sh
@@ -23,7 +23,7 @@ test_expect_success 'setup' '
 	done
 '
 
-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
 	git reset --hard c1 &&
 	i=2 &&
 	refs="" &&
-- 
1.5.6.1.322.ge904b.dirty

^ permalink raw reply related

* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Junio C Hamano @ 2008-07-08  1:17 UTC (permalink / raw)
  To: Josef 'Jeff' Sipek; +Cc: Theodore Ts'o, git
In-Reply-To: <20080708011413.GC1880@josefsipek.net>

"Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:

> A fresh clone (from master.kernel.org over ssh) gets me:
>
> $ git describe
> v1.5.6.2-247-g9237122
> $ git --version
> git version 1.5.6.GIT
> $ git --exec-path
> /home/jeffpc/git-bin/bin
>
> You seem to have a different HEAD for the master branch than I do.

Oh, my private 'master' almost always is ahead of the public one after
work.

But I do not have any new changes around this area lately.  Promise ;-).

^ permalink raw reply

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Junio C Hamano @ 2008-07-08  1:14 UTC (permalink / raw)
  To: Stephan Beyer
  Cc: Jakub Narebski, git, Sam Vilain, Joshua Roys, Sverre Rabbelier,
	Sverre Rabbelier, David Symonds, Lea Wiemann, John Hawley,
	Marek Zawirski, Shawn O. Pearce, Miklos Vajna,
	Johannes Schindelin, Christian Couder, Daniel Barkalow
In-Reply-To: <20080708010050.GD6726@leksak.fem-net>

Stephan Beyer <s-beyer@gmx.net> writes:

> Imho the prototype patchset needs some more review from others.

Yes, very much.  Not just from others and not just from me.

> (Well, I think the
>    http://thread.gmane.org/gmane.comp.version-control.git/86985
> thread died in the last days, but I hope some responses will come and
> also that Junio's patch for cherry-picking root commits will be
> included. I try to be patient...) ;-)

Please don't be patient but actively re-review what you sent out.

I _really_ wanted to merge the basic bits and rewrite of "am" at least to
pu tonight, but I had to drop them after noticing that it does not seem to
handle --rebasing at all (it parses to set $rebasing but after that where
does that bit go?  bash completion wants to see rebasing or applying
markers in .dotest), which made it a non-starter especially I'll be
cooking the other ORIG_HEAD in 'next' as well.

About the "rewrite rebase to use sequencer" bits, because we've dropped
the older rebase-i change, I do not want your series to depend on it.

^ permalink raw reply

* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Josef 'Jeff' Sipek @ 2008-07-08  1:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Theodore Ts'o, git
In-Reply-To: <7vwsjxxkfd.fsf@gitster.siamese.dyndns.org>

On Mon, Jul 07, 2008 at 05:56:54PM -0700, Junio C Hamano wrote:
> "Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:
...
> > jeffpc@freyr:~$ git --version
> > git version 1.5.6.2.247.g923712
> > jeffpc@freyr:~$ git --exec-path
> > /home/jeffpc/git-bin/bin
> 
> Hmm, it is yours that look wrong.
 
Ok.

> With 1.6.0 preview (aka 'master'):
> 
> $ git --version
> git version 1.5.6.2.255.gbed62

A fresh clone (from master.kernel.org over ssh) gets me:

$ git describe
v1.5.6.2-247-g9237122
$ git --version
git version 1.5.6.GIT
$ git --exec-path
/home/jeffpc/git-bin/bin

You seem to have a different HEAD for the master branch than I do.

Josef 'Jeff' Sipek.

-- 
Don't drink and derive. Alcohol and algebra don't mix.

^ permalink raw reply

* Re: [PATCH] Build in merge
From: Junio C Hamano @ 2008-07-08  1:05 UTC (permalink / raw)
  To: Miklos Vajna; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <20080708010007.GA10347@genesis.frugalware.org>

Miklos Vajna <vmiklos@frugalware.org> writes:

> I could imagine the following situations:
>
> 1) A project has code in a repo, some documentation and po files.
>
> The first two can be merged with builtin strategies, the later probably
> needs a custom merge driver.

Per-path merge is probably better handled with custom ll-merge driver
anyway.  See gitattributes(5).

> 2) Someone is not happy with the current recursive strategy and writes
> from scratch a new one. He/she puts it to pull.twohead, so it will be
> tried before recursive.

That is fine.

> To sum up: I am not sure what would be the benefit of being able to
> specify flags for strategies. However, if we want so, it would be good
> to discuss how it should be done.

It wasn't *me* ;-) who wanted to add these "flags".

I think it does not matter what "my-strategy" does unless "-s my-strategy"
(or pull.twohead) is explicitly given by the user, and at that time,
DEFAULT_* options should not matter.  It probably is Ok to allow fast
forward and trivial merges to them.  We'll see.

>> The part that records which strategies were specified from the command
>> line *in what order* via "-s foo" switches should remain list of pointers
>> into "struct strategy", which is called "struct strategy **use_strategies"
>> in the code and corresponds to the $use_strategies variable in the
>> scripted version.  The order of these is important, as that defines in
>> which order the strategies are tried [*1*].  If you go route (1), these
>> pointers will all be pointing at elements in all_strategies[]; with route
>> (2) they may be pointing at either all_strageties[] element or
>> user_strategies[] element.
>
> I see the problem, I lost the order at the moment.

That's Ok.  See the other patch on top of your previous one.

^ permalink raw reply

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Stephan Beyer @ 2008-07-08  1:00 UTC (permalink / raw)
  To: Jakub Narebski
  Cc: git, Sam Vilain, Joshua Roys, Sverre Rabbelier, Sverre Rabbelier,
	David Symonds, Lea Wiemann, John Hawley, Marek Zawirski,
	Shawn O. Pearce, Miklos Vajna, Johannes Schindelin,
	Christian Couder, Daniel Barkalow
In-Reply-To: <200807080227.43515.jnareb@gmail.com>

Hi,

> 6. git-sequencer (!)
> 
> Student: Stephan Beyer
> Mentor: Christian Couder, Daniel Barkalow
> 
> It started with discussion over TODO file format:
>   http://thread.gmane.org/gmane.comp.version-control.git/84230
> 
> Now there is prototype shell script implementation (which has some 
> limitations because it is prototype) in "git sequencer prototype"
>   http://thread.gmane.org/gmane.comp.version-control.git/86985
> 
> Stephan Beyer wrote:
>   I'm using sequencer-based git-am and git-rebase-i and also 
>   git-sequencer itself for around 2-3 weeks now. So, for me, it is
>   reality-proven [...]

That's a nice "summary".
Imho the prototype patchset needs some more review from others.
(Well, I think the
   http://thread.gmane.org/gmane.comp.version-control.git/86985
thread died in the last days, but I hope some responses will come and
also that Junio's patch for cherry-picking root commits will be
included. I try to be patient...) ;-)

You may also add a little note, that I have started the builtin sequencer,
but I cannot provide really big news here :-)

Thanks for your overall git GSoC engagement,
  Stephan

-- 
Stephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F

^ permalink raw reply

* Re: [PATCH] Build in merge
From: Miklos Vajna @ 2008-07-08  1:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7v63rhz03x.fsf@gitster.siamese.dyndns.org>

[-- Attachment #1: Type: text/plain, Size: 3544 bytes --]

On Mon, Jul 07, 2008 at 05:32:50PM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> > So here is a version, this time without the use_strategies list.
> 
> That is not what I meant.  I am afraid perhaps I misunderstood what you
> were talking about.
> 
> When/if you allow user defined new strategies, then you have a choice:
> 
>  (1) find "git-merge-*" in path, add them to the single all_strategies[]
>      list (but you will do the ALLOC_GROW() business so you would need to
>      use the one you currently have as static form to prime the real list),
>      and look for "foo" strategy when "-s foo" is given from that single
>      list, or
> 
>  (2) find "git-merge-*" in path, add them to a separate user_strategies[]
>      list, and look for "foo" strategy when "-s foo" is given from the
>      user_strategies[] list and all_strategies[] list (all_strategies[]
>      should perhaps be renamed to builtin_strategies[] if you go that
>      route).

OK, I see now. Actually I think the primary problem with a custom
strategy is that we do not know its flags, IOW if it handles an octopus,
etc. So I think there are two questions here:

1) How to tell git-merge the flags of a custom strategy? Or: is it
necessary at all?

I could imagine the following situations:

1) A project has code in a repo, some documentation and po files.

The first two can be merged with builtin strategies, the later probably
needs a custom merge driver.

So, in most cases recursive is fine, but sometimes the maintainer wants
to say 'git pull -s po'.

In this case the flags of 'po' does not really matter.

2) Someone is not happy with the current recursive strategy and writes
from scratch a new one. He/she puts it to pull.twohead, so it will be
tried before recursive.

To sum up: I am not sure what would be the benefit of being able to
specify flags for strategies. However, if we want so, it would be good
to discuss how it should be done.

> The part that records which strategies were specified from the command
> line *in what order* via "-s foo" switches should remain list of pointers
> into "struct strategy", which is called "struct strategy **use_strategies"
> in the code and corresponds to the $use_strategies variable in the
> scripted version.  The order of these is important, as that defines in
> which order the strategies are tried [*1*].  If you go route (1), these
> pointers will all be pointing at elements in all_strategies[]; with route
> (2) they may be pointing at either all_strageties[] element or
> user_strategies[] element.

I see the problem, I lost the order at the moment.

> If you are never going to say "available strategies are these" after you
> start supporting user-defined strategy, then you do not necessarily need
> to do the "find 'git-merge-*' in path, add them to ..." step above, in
> which case it would be Ok not to scan the path and add them to
> all_strategies[] (in route (1)) nor user_strategies[] (in route (2)).
> Instead, you would just create a new "struct strategy" instance lazily
> when the user gave "-s foo" and "foo" is not one of the built-in strategy.
> You would put that at the tail of "struct strategy **use_strategy" array,
> and iterate over use_strategy in the order they are given on the command
> line.

I guess it would be nice to adjust the error message and scanning path,
but that's something - I guess - that should be done after the rewrite
is complete and there are no more issues with it.

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Junio C Hamano @ 2008-07-08  0:56 UTC (permalink / raw)
  To: Josef 'Jeff' Sipek; +Cc: Theodore Ts'o, git
In-Reply-To: <20080708003256.GB1880@josefsipek.net>

"Josef 'Jeff' Sipek" <jeffpc@josefsipek.net> writes:

> On Mon, Jul 07, 2008 at 08:03:12PM -0400, Theodore Ts'o wrote:
>> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
>> ---
>>  guilt |    2 +-
>>  1 files changed, 1 insertions(+), 1 deletions(-)
>> 
>> diff --git a/guilt b/guilt
>> index cdf4e9b..d30d35a 100755
>> --- a/guilt
>> +++ b/guilt
>> @@ -23,7 +23,7 @@ esac
>>  # we change directories ourselves
>>  SUBDIRECTORY_OK=1
>>  
>> -. git-sh-setup
>> +. $(git --exec-path)/git-sh-setup
>
> This seems wrong.
>
> jeffpc@freyr:~$ git --version
> git version 1.5.6.2.247.g923712
> jeffpc@freyr:~$ git --exec-path
> /home/jeffpc/git-bin/bin

Hmm, it is yours that look wrong.

With 1.6.0 preview (aka 'master'):

$ git --version
git version 1.5.6.2.255.gbed62
$ git --exec-path
/home/junio/git-master/libexec/git-core

With 1.5.6 maintenance track (aka 'maint'):

$ ~/git-maint/bin/git --version
git version 1.5.6.2.15.g39f31
$ ~/git-maint/bin/git --exec-path
/home/junio/git-maint/bin

^ permalink raw reply

* Re: [PATCH] Build in merge
From: Junio C Hamano @ 2008-07-08  0:53 UTC (permalink / raw)
  To: Miklos Vajna; +Cc: To: Junio C Hamano, git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7v63rhz03x.fsf@gitster.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

> That is not what I meant.  I am afraid perhaps I misunderstood what you
> were talking about.
> ...
> The comparison I gave was between the above two.  But the change you are
> talking about is completely different, isn't it?
>
> The part that records which strategies were specified from the command
> line *in what order* via "-s foo" switches should remain list of pointers
> into "struct strategy", which is called "struct strategy **use_strategies"
> in the code and corresponds to the $use_strategies variable in the
> scripted version.

Here is what I meant to suggest; sorry for confusing you with an initial
typo of having only one pointer in front of use_strategies "array of
pointers".  Applies on top of your previous round.

--

 builtin-merge.c |   58 +++++++++++++++++++++++++++---------------------------
 1 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/builtin-merge.c b/builtin-merge.c
index 7312997..b2e702a 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -44,7 +44,7 @@ static int allow_trivial = 1, have_message;
 static struct strbuf merge_msg;
 static struct commit_list *remoteheads;
 static unsigned char head[20], stash[20];
-static struct strategy *use_strategies;
+static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char *branch;
 
@@ -74,44 +74,44 @@ static int option_parse_message(const struct option *opt,
 	return 0;
 }
 
-static int strategy_lookup(const char *path)
+static struct strategy *get_strategy(const char *name)
 {
 	int i;
 
-	if (!path)
-		return -1;
+	if (!name)
+		return NULL;
 
 	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-		if (!strcmp(path, all_strategy[i].name))
-			return all_strategy[i].attr;
-	return -1;
+		if (!strcmp(name, all_strategy[i].name))
+			return &all_strategy[i];
+	return NULL;
 }
 
-static inline void append_strategy(const char *name, unsigned attr)
+static void append_strategy(struct strategy *s)
 {
 	ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc);
-	use_strategies[use_strategies_nr].name = name;
-	use_strategies[use_strategies_nr++].attr = attr;
+	use_strategies[use_strategies_nr++] = s;
 }
 
 static int option_parse_strategy(const struct option *opt,
-	const char *arg, int unset)
+				 const char *name, int unset)
 {
-	int i, attr;
+	int i;
+	struct strategy *s;
 
 	if (unset)
 		return 0;
 
-	attr = strategy_lookup(arg);
+	s = get_strategy(name);
 
-	if (attr >= 0)
-		append_strategy(arg, attr);
+	if (s)
+		append_strategy(s);
 	else {
 		struct strbuf err;
 		strbuf_init(&err, 0);
 		for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
 			strbuf_addf(&err, " %s", all_strategy[i].name);
-		fprintf(stderr, "Could not find merge strategy '%s'.\n", arg);
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
 		fprintf(stderr, "Available strategies are:%s.\n", err.buf);
 		exit(1);
 	}
@@ -643,18 +643,18 @@ static void add_strategies(const char *string, unsigned attr)
 	split_merge_strategies(string, &list, &list_nr, &list_alloc);
 	if (list != NULL) {
 		for (i = 0; i < list_nr; i++) {
-			int attr;
+			struct strategy *s;
 
-			attr = strategy_lookup(list[i].name);
-			if (attr >= 0)
-				append_strategy(list[i].name, attr);
+			s = get_strategy(list[i].name);
+			if (s)
+				append_strategy(s);
 		}
 		return;
 	}
 	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
 		if (all_strategy[i].attr & attr)
-			append_strategy(all_strategy[i].name,
-				all_strategy[i].attr);
+			append_strategy(&all_strategy[i]);
+
 }
 
 static int merge_trivial(void)
@@ -903,9 +903,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 
 	for (i = 0; i < use_strategies_nr; i++) {
-		if (use_strategies[i].attr & NO_FAST_FORWARD)
+		if (use_strategies[i]->attr & NO_FAST_FORWARD)
 			allow_fast_forward = 0;
-		if (use_strategies[i].attr & NO_TRIVIAL)
+		if (use_strategies[i]->attr & NO_TRIVIAL)
 			allow_trivial = 0;
 	}
 
@@ -1043,14 +1043,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		}
 		if (use_strategies_nr != 1)
 			printf("Trying merge strategy %s...\n",
-				use_strategies[i].name);
+				use_strategies[i]->name);
 		/*
 		 * Remember which strategy left the state in the working
 		 * tree.
 		 */
-		wt_strategy = use_strategies[i].name;
+		wt_strategy = use_strategies[i]->name;
 
-		ret = try_merge_strategy(use_strategies[i].name,
+		ret = try_merge_strategy(use_strategies[i]->name,
 			common, head_arg);
 		if (!option_commit && !ret) {
 			merge_was_ok = 1;
@@ -1072,7 +1072,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 				int cnt = evaluate_result();
 
 				if (best_cnt <= 0 || cnt <= best_cnt) {
-					best_strategy = use_strategies[i].name;
+					best_strategy = use_strategies[i]->name;
 					best_cnt = cnt;
 				}
 			}
@@ -1106,7 +1106,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 				"No merge strategy handled the merge.\n");
 		else
 			fprintf(stderr, "Merge with strategy %s failed.\n",
-				use_strategies[0].name);
+				use_strategies[0]->name);
 		return 2;
 	} else if (best_strategy == wt_strategy)
 		; /* We already have its result in the working tree. */

^ permalink raw reply related

* Re: [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: David Symonds @ 2008-07-08  0:43 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Sverre Rabbelier
In-Reply-To: <200807080227.43515.jnareb@gmail.com>

On Tue, Jul 8, 2008 at 10:27 AM, Jakub Narebski <jnareb@gmail.com> wrote:

> I'd very much like to have (or perhaps to wrote) some sort of interim
> progress report for Google Summer of Code 2008 projects on
> http://git.or.cz/gitwiki/SoC2008Projects.  Therefore I'd like you to
> expand and/or correct those mini-summaries below.

[.. snip ..]

> 2. git-statistics (?)
>
> Student: Sverre Rabbelier
> Mentor: David Symonds
>
> There were some posts about how git-statistics can be used:
>  http://thread.gmane.org/gmane.comp.version-control.git/81534
>  http://thread.gmane.org/gmane.comp.version-control.git/82027
> but it was mainly theoretical thread
>
> The git-stats repository at http://repo.or.cz/w/git-stats.git (gitweb)
> has some commits, but I don't remember any of them on git mailing list.
> Not ready?

Sverre is in the process of writing up a progress report for the Git
ML; expect it by the end of this week.



Dave.

^ permalink raw reply

* Re: [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Josef 'Jeff' Sipek @ 2008-07-08  0:32 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: git
In-Reply-To: <1215475392-26221-3-git-send-email-tytso@mit.edu>

On Mon, Jul 07, 2008 at 08:03:12PM -0400, Theodore Ts'o wrote:
> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
> ---
>  guilt |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)
> 
> diff --git a/guilt b/guilt
> index cdf4e9b..d30d35a 100755
> --- a/guilt
> +++ b/guilt
> @@ -23,7 +23,7 @@ esac
>  # we change directories ourselves
>  SUBDIRECTORY_OK=1
>  
> -. git-sh-setup
> +. $(git --exec-path)/git-sh-setup

This seems wrong.


jeffpc@freyr:~$ git --version
git version 1.5.6.2.247.g923712
jeffpc@freyr:~$ git --exec-path
/home/jeffpc/git-bin/bin

AFAICT, it should be:

/home/jeffpc/git-bin/libexec/git-core

(which contains git-sh-setup)

Josef 'Jeff' Sipek.

-- 
I think there is a world market for maybe five computers.
		- Thomas Watson, chairman of IBM, 1943.

^ permalink raw reply

* Re: [PATCH] Build in merge
From: Junio C Hamano @ 2008-07-08  0:32 UTC (permalink / raw)
  To: Miklos Vajna; +Cc: To: Junio C Hamano, git, Johannes Schindelin, Olivier Marin
In-Reply-To: <1215474140-11220-1-git-send-email-vmiklos@frugalware.org>

Miklos Vajna <vmiklos@frugalware.org> writes:

> Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
> ---
>
> On Mon, Jul 07, 2008 at 11:15:09AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
>> I do not get you on this point.  Which one is nicer?
>>
>>  (1) Have two lists, perhaps all_* and user_*.  The logic that finds a
>>      strategy searches in two lists.  The logic that checks if a given
>>      strategy is built-in checks if it is on all_* list.
>>
>>  (2) Have a single list, but add a boolean "unsigned is_builtin:1" to
>>  each
>>      element of it.  The logic that finds a strategy looks in this
>>      single
>>      list.  The logic that checks if a given strategy is built-in
>>      looks at
>>      the strategy instance and it has the bit already.
>>
>> You seem to be advocating (1) but I do not understand why...
>
> Ah, OK. For now, I just added an "unsigned enabled:1;". Later we can add
> an "unsigned is_buildin:1;" as well, but currently we die with earlier
> with a "Could not find merge strategy" error message, so is_builtin
> would be always true.
>
> So here is a version, this time without the use_strategies list.

That is not what I meant.  I am afraid perhaps I misunderstood what you
were talking about.

When/if you allow user defined new strategies, then you have a choice:

 (1) find "git-merge-*" in path, add them to the single all_strategies[]
     list (but you will do the ALLOC_GROW() business so you would need to
     use the one you currently have as static form to prime the real list),
     and look for "foo" strategy when "-s foo" is given from that single
     list, or

 (2) find "git-merge-*" in path, add them to a separate user_strategies[]
     list, and look for "foo" strategy when "-s foo" is given from the
     user_strategies[] list and all_strategies[] list (all_strategies[]
     should perhaps be renamed to builtin_strategies[] if you go that
     route).

The comparison I gave was between the above two.  But the change you are
talking about is completely different, isn't it?

The part that records which strategies were specified from the command
line *in what order* via "-s foo" switches should remain list of pointers
into "struct strategy", which is called "struct strategy **use_strategies"
in the code and corresponds to the $use_strategies variable in the
scripted version.  The order of these is important, as that defines in
which order the strategies are tried [*1*].  If you go route (1), these
pointers will all be pointing at elements in all_strategies[]; with route
(2) they may be pointing at either all_strageties[] element or
user_strategies[] element.

If you are never going to say "available strategies are these" after you
start supporting user-defined strategy, then you do not necessarily need
to do the "find 'git-merge-*' in path, add them to ..." step above, in
which case it would be Ok not to scan the path and add them to
all_strategies[] (in route (1)) nor user_strategies[] (in route (2)).
Instead, you would just create a new "struct strategy" instance lazily
when the user gave "-s foo" and "foo" is not one of the built-in strategy.
You would put that at the tail of "struct strategy **use_strategy" array,
and iterate over use_strategy in the order they are given on the command
line.


[Footnote]

*1* Personally, I find the importance of this dubious in practice, as I
said earlier, I do not think it would work well to try different
strategies and pick the best one --- evaluating which result is the *best*
is difficult.  If you want to stay compatible with the scripted version,
however, you cannot just mark entries in all_strategies[] with boolean and
iterate over them in the order that all_strageties[] define them.  You
need to try them in the order the user specified.

^ permalink raw reply

* [GSoC] What is status of Git's Google Summer of Code 2008 projects?
From: Jakub Narebski @ 2008-07-08  0:27 UTC (permalink / raw)
  To: git
  Cc: Sam Vilain, Joshua Roys, Sverre Rabbelier, Sverre Rabbelier,
	David Symonds, Lea Wiemann, John Hawley, Marek Zawirski,
	Shawn O. Pearce, Miklos Vajna, Johannes Schindelin, Stephan Beyer,
	Christian Couder, Daniel Barkalow

I'd very much like to have (or perhaps to wrote) some sort of interim 
progress report for Google Summer of Code 2008 projects on 
http://git.or.cz/gitwiki/SoC2008Projects.  Therefore I'd like you to 
expand and/or correct those mini-summaries below.

(It would be, I guess, good preparation for GSoC 2008 mid-term 
evaluations, which according to GSoC 2008 timeline
  http://code.google.com/opensource/gsoc/2008/faqs.html#0.1_timeline
are to take place July 7 -- July 14.)


1. GitTorrent (???)

Student: Joshua Roys
Mentor: Sam Vilain

There was short thread of me asking about project
  http://thread.gmane.org/gmane.comp.version-control.git/83611
where I got gittorrent mailing list (no activity at least according to 
list archive http://lists.utsl.gen.nz/pipermail/gittorrent/) and URL 
for project repo / gitweb... which is currently down, so I cannot check 
if there is anything here.

What is the status of this project, please?

http://www.codinghorror.com/blog/archives/001134.html ("Don't Got Dark")


2. git-statistics (?)

Student: Sverre Rabbelier
Mentor: David Symonds

There were some posts about how git-statistics can be used:
  http://thread.gmane.org/gmane.comp.version-control.git/81534
  http://thread.gmane.org/gmane.comp.version-control.git/82027
but it was mainly theoretical thread

The git-stats repository at http://repo.or.cz/w/git-stats.git (gitweb) 
has some commits, but I don't remember any of them on git mailing list.
Not ready?


3. Gitweb caching

Student: Lea Wiemann
Mentor: John 'warthog' Hawley

Lea has chosen caching data and memcached as primary caching engine,
and is working on object layer on top of Git.pm, namely Git::Repo and 
friends, which will be used by gitweb.  If I understand correctly 
caching is to be done, or at least helped by this layer.

There were added tests for Git.pm (now in git.git I think) and Mechanize 
test for gitweb (which detected a few errors).

Is it correct?


4. Eclipse plugin push support (!)

Student: Marek Zawirski
Mentor: Shawn O. Pearce

I am not following egit/jgit development close enough, but if I remember 
correctly there is some code which provides very rudimentary support 
for native generation of simplified packs, and IIRC also for push over 
some protocols.

And there is push support over SFTP and (encrypted) Amazon S3...


5. git-merge builtin (!!!)

Student: Miklos Vajna
Mentor: Johannes Schindelin

Builtin merge, together with preparation patches, is now at n-th 
iteration, as shown in the "Build in merge" thread:
  http://thread.gmane.org/gmane.comp.version-control.git/86584

In "What's cooking in git.git (topics)" Junio C Hamano wrote:

  It already is beginning to become clear what 1.6.0 will look like.
  [...]
  * git-merge will be rewritten in C.


6. git-sequencer (!)

Student: Stephan Beyer
Mentor: Christian Couder, Daniel Barkalow

It started with discussion over TODO file format:
  http://thread.gmane.org/gmane.comp.version-control.git/84230

Now there is prototype shell script implementation (which has some 
limitations because it is prototype) in "git sequencer prototype"
  http://thread.gmane.org/gmane.comp.version-control.git/86985

Stephan Beyer wrote:
  I'm using sequencer-based git-am and git-rebase-i and also 
  git-sequencer itself for around 2-3 weeks now. So, for me, it is
  reality-proven [...]

-- 
Jakub Narebski
Poland

^ permalink raw reply

* Two quick patches for guilt v0.30
From: Theodore Ts'o @ 2008-07-08  0:03 UTC (permalink / raw)
  To: jeffpc; +Cc: git

Hi Jeff, here are two quick patches for guilt, versus v0.30.  The
second is needed for people using the latest git (on the master
branch), so it'd be great if you could get this out.

Thanks, regards,

					- Ted

^ permalink raw reply

* [PATCH 2/2] Make guilt work even after git-sh-setup is moved out of the user's path
From: Theodore Ts'o @ 2008-07-08  0:03 UTC (permalink / raw)
  To: jeffpc; +Cc: git, Theodore Ts'o
In-Reply-To: <1215475392-26221-2-git-send-email-tytso@mit.edu>

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
---
 guilt |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/guilt b/guilt
index cdf4e9b..d30d35a 100755
--- a/guilt
+++ b/guilt
@@ -23,7 +23,7 @@ esac
 # we change directories ourselves
 SUBDIRECTORY_OK=1
 
-. git-sh-setup
+. $(git --exec-path)/git-sh-setup
 
 #
 # Git version check
-- 
1.5.6.1.205.ge2c7.dirty

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox