Hi,
The idea (by Dscho) is simple: builtin-merge-recursive already has a
merge_recursive() function that is _almost_ ready to be called from
builtins without fork+exec, so make use of it.
And yes, of course this is not for 1.6.0, I just wanted to send this out
before the "suggested 'pencils down' date" of GSoC.
Miklos Vajna (2):
merge-recursive: prepare merge_recursive() to be called from builtins
builtin-merge: avoid run_command_v_opt() for recursive
builtin-merge-recursive.c | 19 +++++++---
builtin-merge.c | 82 +++++++++++++++++++++++++++++++--------------
2 files changed, 70 insertions(+), 31 deletions(-)
When other builtins call merge_recursive(), they would have to handle
the GIT_MERGE_VERBOSITY environment variable, causing a code
duplication. Same story for the git_config() call. It's better to do it
when merge_recursive() is called the first time.
Signed-off-by: Miklos Vajna <redacted>
---
builtin-merge-recursive.c | 19 +++++++++++++------
1 files changed, 13 insertions(+), 6 deletions(-)
The try_merge_strategy() function always ran the strategy in a separate
process, though this is not always necessary. The recursive strategy can
be called without a fork(). This patch adds a check, and calls recursive
in the same process without wasting resources.
Signed-off-by: Johannes Schindelin <redacted>
Signed-off-by: Miklos Vajna <redacted>
---
Adding the signoff of Dscho as well, as he wrote the last hunk.
builtin-merge.c | 82 ++++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 57 insertions(+), 25 deletions(-)
@@ -511,28 +512,55 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,structcommit_list*j;structstrbufbuf;-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;+if(!strcmp(strategy,"recursive")){+intclean;+structcommit*result;+structlock_file*lock=xcalloc(1,sizeof(structlock_file));+intindex_fd;+structcommit_list*reversed=NULL;++if(remoteheads->next){+error("Not handling anything other than two heads merge.");+return2;+}++for(j=common;j;j=j->next)+commit_list_insert(j->item,&reversed);++index_fd=hold_locked_index(lock,1);+clean=merge_recursive(lookup_commit(head),+remoteheads->item,head_arg,+(constchar*)remoteheads->item->util,+reversed,&result);+if(active_cache_changed&&+(write_cache(index_fd,active_cache,active_nr)||+commit_locked_index(lock)))+die("unable to write %s",get_index_file());+returnclean?0:1;+}else{+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;+}}staticvoidcount_diff_files(structdiff_queue_struct*q,
@@ -670,7 +698,9 @@ static int finish_automerge(struct commit_list *common,structstrbufbuf=STRBUF_INIT;unsignedcharresult_commit[20];-free_commit_list(common);+if(strcmp(wt_strategy,"recursive"))+/* recursive already freed it */+free_commit_list(common);if(allow_fast_forward){parents=remoteheads;commit_list_insert(lookup_commit(head),&parents);
@@ -873,12 +903,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)for(i=0;i<argc;i++){structobject*o;+structcommit*commit;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;+commit=lookup_commit(o->sha1);+commit->util=(void*)argv[i];+remotes=&commit_list_insert(commit,remotes)->next;strbuf_addf(&buf,"GITHEAD_%s",sha1_to_hex(o->sha1));setenv(buf.buf,argv[i],1);
From: Stephan Beyer <hidden> Date: 2016-06-15 22:45:08
Cherry-pick and revert always ran the merging in a separate process.
This patch makes cherry-pick/revert call merge_recursive() instead
of running git-merge-recursive.
Signed-off-by: Stephan Beyer <redacted>
---
Hi,
I wonder if this patch fits in line.
builtin-merge-recursive.c | 2 +-
builtin-revert.c | 41 ++++++++++++++++++++++-------------------
merge-recursive.h | 1 +
3 files changed, 24 insertions(+), 20 deletions(-)
From: Stephan Beyer <hidden> Date: 2016-06-15 22:45:08
Hi,
Miklos Vajna wrote:
When other builtins call merge_recursive(), they would have to handle
the GIT_MERGE_VERBOSITY environment variable, causing a code
duplication. Same story for the git_config() call. It's better to do it
when merge_recursive() is called the first time.
Hmm, I have the long-run vision that we have a nice libgit some day,
with merge_recursive() being part of it. And I'm a little unsure if
libified functions should rely on environment variables.
So I'm wondering if the verbosity should be set in the caller functions
of merge_recursive(), i.e. that cmd_merge_recursive() and cmd_merge()
(or another part of builtin-merge.c) does the
getenv("GIT_MERGE_VERBOSITY")
stuff and a verbosity value could be a new argument to merge_recursive().
Then other merge_recursive() users don't need to
setenv("GIT_MERGE_VERBOSITY", "3", 1)
or something, they just pass 3 as the verbosity value.
Just a thought,
Stephan
PS: Your patch looks fine to me.
--
Stephan Beyer [off-list ref], PGP 0x6EDDD207FCC5040F
@@ -219,15 +216,21 @@ static int merge_recursive(const char *base_sha1,*and$prevontopofus(whenreverting),orthechangebetween*$prevand$commitontopofus(whencherry-pickingorreplaying).*/-argv[i++]="merge-recursive";-if(base_sha1)-argv[i++]=base_sha1;-argv[i++]="--";-argv[i++]=head_sha1;-argv[i++]=next_sha1;-argv[i++]=NULL;--returnrun_command_v_opt(argv,RUN_COMMAND_NO_STDIN|RUN_GIT_CMD);+if(base_sha1){+structcommit*base=get_ref(base_sha1);+commit_list_insert(base,&ca);+}+h1=get_ref(head_sha1);+h2=get_ref(next_sha1);++index_fd=hold_locked_index(lock,1);+clean=merge_recursive(h1,h2,head_name,next_name,ca,&result);
h1 and h2 are not expressive. head_commit and next_commit would be.
Rest looks good to me -- even if I had to spend too much time (therefore
being not really thorough in the end) verifying that merge_recursive()
does not lock the index itself, and that GITHEAD_* definitions are not
necessary anymore, since merge_recursive() takes the arguments directly;
you might want to make it easier for this reviewer in the future, if you
want this reviewer to review your patches, that is.
Ciao,
Dscho
On Mon, Aug 11, 2008 at 05:13:03PM +0200, Stephan Beyer [off-list ref] wrote:
Hmm, I have the long-run vision that we have a nice libgit some day,
That would be nice, but in that case I would start avoiding die() which
is an awful amount of work... (There were multiple threads on the list
previously.)
with merge_recursive() being part of it. And I'm a little unsure if
libified functions should rely on environment variables.
Well, many libs do this. Random example: ld.so relies on LD_LIBRARY_PATH
as well.
Anyway sure, using function parameters instead of env vars is more
elegant.
From: Junio C Hamano <hidden> Date: 2016-06-15 22:45:08
Miklos Vajna [off-list ref] writes:
The try_merge_strategy() function always ran the strategy in a separate
process, though this is not always necessary. The recursive strategy can
be called without a fork(). This patch adds a check, and calls recursive
in the same process without wasting resources.
Yes, it saves a fork, but is this really worth it in the bigger picture?
Doesn't the current code structure have benefit of allowing git-merge
itself do necessary clean-up action when merge-recursive calls any of the
die() it has in many places?
The name get_ref() is way too generic to be non-static.
That's right.
But I have a hunch that peel_to_type() does a lot of what we want here,
if not all of it.
get_ref() has a big advantage over peel_to_type(): it can handle trees,
via "virtual commits" (make_virtual_commit()).
If you wonder where we need to handle trees on cherry-pick/revert:
With the -n (no commit) option you are allowed to have a dirty index.
So the recursive merge is not done using the HEAD commit but using the
uncommitted tree of the index.
Well, a good alternative could be to just make the really cool
make_virtual_commit() function non-static.
The name could be generic enough. Is it? :-)
Or perhaps: make_virtual_commit_from_tree().
Btw I also need get_ref() (or make_virtual_commit()) for threeway
fallback of the sequencer "patch -3" instruction ("git am -3"). ;)
h1 and h2 are not expressive. head_commit and next_commit would be.
This is also quite true.
Those names, also "ca", were taken from cmd_merge_recursive().
(This is no excuse, just an explanation.)
Rest looks good to me -- even if I had to spend too much time (therefore
being not really thorough in the end) verifying that merge_recursive()
does not lock the index itself,
I can't help here. Miklos has the same change in patch v2/2 and I
wonder if you really expect that I don't test my patches, because
a double lock wouldn't have worked.
and that GITHEAD_* definitions are not necessary anymore, since merge_recursive()
takes the arguments directly;
Ok, I hoped that would've been clear by using head_name/next_name
directly in the merge_recursive() arguments, but nevertheless
thanks for your comment, ...because: using get_ref() the GITHEAD_*
definitions *are* still needed, because it takes the GITHEAD_*
name for the virtual commits...
Under this additional circumstance, I really tend to make
make_virtual_commit() non-static.
Kind regards,
Stephan
--
Stephan Beyer [off-list ref], PGP 0x6EDDD207FCC5040F
On Mon, Aug 11, 2008 at 11:47:07AM -0700, Junio C Hamano [off-list ref] wrote:
Yes, it saves a fork, but is this really worth it in the bigger picture?
Doesn't the current code structure have benefit of allowing git-merge
itself do necessary clean-up action when merge-recursive calls any of the
die() it has in many places?
As far as I see in most cases merge-recursive does not call die().
Cases when it does are like:
- broken snprintf
- cache_tree_fully_valid() or cache_tree_update() fails
- diff_setup_done() fails
- flush_buffer() fails
- read_sha1_file() fails
- ll_merge() fails
etc.
In short, I think there are two cases when a die() would be problematic
inside merge-recursive when calling it from builtin-merge:
- merge-recursive can't handle a merge, but an other strategy could do.
This is the case when doing an octopus merge but in that case
merge_recursive() is not called at all.
- merge-recursive results in conflicts, but an other strategy could
handle the merge without conflicts. In this case die() isn't used
either, so this will not be a problem.
So I don't think there is a case when a die() inside merge-recursive
would occur, but an other strategy would handle the merge properly.
Unless I missed something. ;-)
On Mon, Aug 11, 2008 at 09:01:23PM +0200, Stephan Beyer [off-list ref] wrote:
Well, a good alternative could be to just make the really cool
make_virtual_commit() function non-static.
The name could be generic enough. Is it? :-)
Or perhaps: make_virtual_commit_from_tree().
Given that you can't make virtual commits from commits, tags or blobs, I
think the name "as is" generic enough.
From: Stephan Beyer <hidden> Date: 2016-06-15 22:45:08
Cherry-pick and revert always ran the merging in a separate process.
This patch makes cherry-pick/revert call merge_recursive() instead
of running git-merge-recursive.
To be able to cherry-pick/revert -n (without committing) on a dirty
index, make_virtual_commit() is needed and thus declared non-static.
Also the GITHEAD_* environment definitions are not needed anymore,
since the names are direct arguments to make_virtual_commit() and
merge_recursive().
Signed-off-by: Stephan Beyer <redacted>
---
Hi,
so I give it a new try.
builtin-merge-recursive.c | 2 +-
builtin-revert.c | 56 +++++++++++++++++++++++++-------------------
merge-recursive.h | 2 +
3 files changed, 35 insertions(+), 25 deletions(-)
@@ -42,7 +42,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)*-*(int*)commit->object.sha1settothevirtualid.*/-staticstructcommit*make_virtual_commit(structtree*tree,constchar*comment)+structcommit*make_virtual_commit(structtree*tree,constchar*comment){structcommit*commit=xcalloc(1,sizeof(structcommit));staticunsignedvirtual_id=1;
Is this a mistake that some forward declarations in header files are not
declared "extern"?
Regards,
Stephan
--
Stephan Beyer [off-list ref], PGP 0x6EDDD207FCC5040F