* 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
* [PATCH 1/2] must_commit_first: Make sure the index is up to date
From: Theodore Ts'o @ 2008-07-08 0:03 UTC (permalink / raw)
To: jeffpc; +Cc: git, Theodore Ts'o
In-Reply-To: <1215475392-26221-1-git-send-email-tytso@mit.edu>
git diff-files can be confused if the index is not up to date, so make
sure it is updated first.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
---
guilt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/guilt b/guilt
index 50414a4..cdf4e9b 100755
--- a/guilt
+++ b/guilt
@@ -495,6 +495,7 @@ s/^\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\) \([0-9]\{2\}\):\([0-9]\{2\}\):\(
# usage: must_commit_first
must_commit_first()
{
+ git update-index --refresh --unmerged -q > /dev/null
[ `git diff-files | wc -l` -eq 0 ]
return $?
}
--
1.5.6.1.205.ge2c7.dirty
^ permalink raw reply related
* [PATCH] Build in merge
From: Miklos Vajna @ 2008-07-07 23:42 UTC (permalink / raw)
To: To: Junio C Hamano; +Cc: git, Johannes Schindelin, Olivier Marin
In-Reply-To: <7vhcb14l3m.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 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.
Interdiff: git diff 638c51f..1e74660
Makefile | 2 +-
builtin-merge.c | 1142 +++++++++++++++++++++++++
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, 1146 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..6d801dd
--- /dev/null
+++ b/builtin-merge.c
@@ -0,0 +1,1142 @@
+/*
+ * 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;
+ unsigned enabled:1;
+};
+
+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 const char *branch;
+
+static struct strategy all_strategy[] = {
+ { "recur", NO_TRIVIAL, 0 },
+ { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL, 0 },
+ { "octopus", DEFAULT_OCTOPUS, 0 },
+ { "resolve", 0, 0 },
+ { "stupid", 0, 0 },
+ { "ours", NO_FAST_FORWARD | NO_TRIVIAL, 0 },
+ { "subtree", NO_FAST_FORWARD | NO_TRIVIAL, 0 },
+};
+/*
+ * This contains the number of enabled variables, so you don't have to
+ * scan through all_strategies all the time.
+ */
+static int enabled_strategies;
+
+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 void enable_strategy(const char *name)
+{
+ int i;
+ struct strbuf err;
+
+ enabled_strategies++;
+ for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+ if (!strcmp(all_strategy[i].name, name)) {
+ all_strategy[i].enabled = 1;
+ return;
+ }
+
+ 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);
+}
+
+static int option_parse_strategy(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset)
+ return 0;
+
+ enable_strategy(arg);
+ 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", &all_strategy, "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++)
+ enable_strategy(list[i].name);
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
+ if (all_strategy[i].attr & attr)
+ enable_strategy(all_strategy[i].name);
+}
+
+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, first_strategy = 1;
+ 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 (!enabled_strategies) {
+ if (!remoteheads->next)
+ add_strategies(pull_twohead, DEFAULT_TWOHEAD);
+ else
+ add_strategies(pull_octopus, DEFAULT_OCTOPUS);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(all_strategy); i++) {
+ if (!all_strategy[i].enabled)
+ continue;
+ if (all_strategy[i].attr & NO_FAST_FORWARD)
+ allow_fast_forward = 0;
+ if (all_strategy[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 (enabled_strategies != 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 < ARRAY_SIZE(all_strategy); i++) {
+ int ret;
+ if (!all_strategy[i].enabled)
+ continue;
+ if (first_strategy) {
+ first_strategy = 0;
+ printf("Rewinding the tree to pristine...\n");
+ restore_state();
+ }
+ if (enabled_strategies != 1)
+ printf("Trying merge strategy %s...\n",
+ all_strategy[i].name);
+ /*
+ * Remember which strategy left the state in the working
+ * tree.
+ */
+ wt_strategy = all_strategy[i].name;
+
+ ret = try_merge_strategy(all_strategy[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 = all_strategy[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 (enabled_strategies > 1)
+ fprintf(stderr,
+ "No merge strategy handled the merge.\n");
+ else
+ fprintf(stderr, "Merge with strategy %s failed.\n",
+ wt_strategy);
+ 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: [FIXED PATCH] Make rebase save ORIG_HEAD if changing current branch
From: Jakub Narebski @ 2008-07-07 22:58 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git List, Brian Gernhardt
In-Reply-To: <7vk5fx1g0a.fsf@gitster.siamese.dyndns.org>
Junio C Hamano wrote:
> Jakub Narebski <jnareb@gmail.com> writes:
>
>>> @@ -289,10 +299,10 @@ notation is used. E.g. "`{caret}r1 r2`" means commits reachable
>>> from `r2` but exclude the ones reachable from `r1`.
>>>
>>> This set operation appears so often that there is a shorthand
>>> -for it. "`r1..r2`" is equivalent to "`{caret}r1 r2`". It is
>>> -the difference of two sets (subtract the set of commits
>>> -reachable from `r1` from the set of commits reachable from
>>> -`r2`).
>>> +for it. When you have two commits `r1` and `r2` (named according
>>> +to the syntax explained in SPECIFYING REVISIONS above), you can ask
>>> +for commits that are reachable from r2 but not from r1 by
>>> +"`{caret}r1 r2`" and it can be written as "`r1..r2`".
>>
>> I'm not sure if the last part is improvement, and it wouldn't be better
>> to say rather than r1..r2 / ^r1 r2 are "commits that are reachable from
>> r2, excluding those commits which are reachable from r1" (which translates
>> into set difference / subtracting set of commits.
>
> I tried to make it easier to understand by people without having to know
> what a set difference is, and that was the reason I did not use "subtract"
> nor "difference", as I saw somebody was quoting the above part in #git was
> wondering what it was talking about.
I understand, and the replacement you proposed is better, as it does
not require understanding of [mathematical] set operations. I just
think that "commits that are reachable from r2, excluding those commits
which are reachable from r1" could be better than "commits that are
reachable from r2 but not from r1".
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: [RFC/PATCH (WIP)] Git.pm: Add get_config() method and related subroutines
From: Lea Wiemann @ 2008-07-07 19:24 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git, Petr Baudis
In-Reply-To: <200807040145.14724.jnareb@gmail.com>
Jakub Narebski wrote:
> I'd rather have both functional (of sorts) and object interface.
I wouldn't, since it makes the code more complicated. Having an
object-oriented interface is cleaner (in this case), and it doesn't make
it more complicated to use; hence I don't think there's any need for a
functional interface.
> Git::Repo / Git::Config could use methods / subroutines from Git.pm;
I don't think that's possible, since Git.pm has a pretty long-running
(and complicated) instantiation method, whereas Git::Repo has (and must
have) instantaneous instantiation (without system calls). Also, Git.pm
is so strange (design-wise) that I'm not very happy with depending on it
as it is. I'll write more about that later though.
^ permalink raw reply
* Re: [PATCH] Skip unwritable tests for root
From: Lea Wiemann @ 2008-07-07 19:14 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Fernando J. Pereda, git, Jakub Narebski
In-Reply-To: <7vabh31e9p.fsf@gitster.siamese.dyndns.org>
Junio C Hamano wrote:
> If we were to do anything, don't we rather want to fail everything upfront
> when the tests are run by root [because it's crazy]?
I think that unless/until someone wants to submit a patch that makes the
tests fail upfront, we should use Fernando's patch; it's an improvement
over the current situation.
I'm actually not entirely sure if skipping or failing everything if the
tests are run as root is a good idea -- for instance, neither skipping
nor failing seems appropriate for Gentoo's root-in-a-sandbox situation.
Jakub Narebski wrote:
> "Fernando J. Pereda" <ferdy@ferdyx.org> writes:
>> + test_expect_success '[...], skipping test' :
>> + test_done
>> + exit
>
> Yet another place that would profit from test_skip in test-lib.sh...
*nods* I'd rather name it 'test_skip_all' for clarity though, at least
if it contains "test_done; exit". (I'm not volunteering to send a
patch, FTR...)
^ permalink raw reply
* Re: [FIXED PATCH] Make rebase save ORIG_HEAD if changing current branch
From: Junio C Hamano @ 2008-07-07 22:33 UTC (permalink / raw)
To: Jakub Narebski; +Cc: Git List, Brian Gernhardt
In-Reply-To: <m3tzf1e3ze.fsf@localhost.localdomain>
Jakub Narebski <jnareb@gmail.com> writes:
>> @@ -289,10 +299,10 @@ notation is used. E.g. "`{caret}r1 r2`" means commits reachable
>> from `r2` but exclude the ones reachable from `r1`.
>>
>> This set operation appears so often that there is a shorthand
>> -for it. "`r1..r2`" is equivalent to "`{caret}r1 r2`". It is
>> -the difference of two sets (subtract the set of commits
>> -reachable from `r1` from the set of commits reachable from
>> -`r2`).
>> +for it. When you have two commits `r1` and `r2` (named according
>> +to the syntax explained in SPECIFYING REVISIONS above), you can ask
>> +for commits that are reachable from r2 but not from r1 by
>> +"`{caret}r1 r2`" and it can be written as "`r1..r2`".
>
> I'm not sure if the last part is improvement, and it wouldn't be better
> to say rather than r1..r2 / ^r1 r2 are "commits that are reachable from
> r2, excluding those commits which are reachable from r1" (which translates
> into set difference / subtracting set of commits.
I tried to make it easier to understand by people without having to know
what a set difference is, and that was the reason I did not use "subtract"
nor "difference", as I saw somebody was quoting the above part in #git was
wondering what it was talking about.
^ permalink raw reply
* Re: [FIXED PATCH] Make rebase save ORIG_HEAD if changing current branch
From: Jakub Narebski @ 2008-07-07 22:14 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git List, Brian Gernhardt
In-Reply-To: <7vod591hlp.fsf@gitster.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> writes:
> -- >8 --
> Documentation: update sections on naming revisions and revision ranges
[...]
> While at it, reword the explanation on r1..r2 notation to reduce
> confusion.
> diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
> index 378a312..7184274 100644
> --- a/Documentation/git-rev-parse.txt
> +++ b/Documentation/git-rev-parse.txt
> @@ -289,10 +299,10 @@ notation is used. E.g. "`{caret}r1 r2`" means commits reachable
> from `r2` but exclude the ones reachable from `r1`.
>
> This set operation appears so often that there is a shorthand
> -for it. "`r1..r2`" is equivalent to "`{caret}r1 r2`". It is
> -the difference of two sets (subtract the set of commits
> -reachable from `r1` from the set of commits reachable from
> -`r2`).
> +for it. When you have two commits `r1` and `r2` (named according
> +to the syntax explained in SPECIFYING REVISIONS above), you can ask
> +for commits that are reachable from r2 but not from r1 by
> +"`{caret}r1 r2`" and it can be written as "`r1..r2`".
I'm not sure if the last part is improvement, and it wouldn't be better
to say rather than r1..r2 / ^r1 r2 are "commits that are reachable from
r2, excluding those commits which are reachable from r1" (which translates
into set difference / subtracting set of commits.
--
Jakub Narebski
Poland
ShadeHawk on #git
^ permalink raw reply
* [PATCH] gitweb: Describe projects_index format in more detail
From: Jakub Narebski @ 2008-07-07 22:07 UTC (permalink / raw)
To: git
Update and extend information about $projects_list file format in
gitweb/README and in gitweb/INSTALL.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This was part of
"gitweb: Extend project_index file format by project description"
patch send to git mailing list (but not applied). I thought that
updating documentation can be done independently, so here it is (minus
of course information about optional repository description field).
gitweb/INSTALL | 6 ++++++
gitweb/README | 46 ++++++++++++++++++++++++++++++++++++++++------
2 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index f7194db..26967e2 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -144,6 +144,12 @@ Gitweb repositories
Spaces in both project path and project owner have to be encoded as either
'%20' or '+'.
+ Other characters that have to be url-encoded, i.e. replaced by '%'
+ followed by two-digit character number in octal, are: other whitespace
+ characters (because they are field separator in a record), plus sign '+'
+ (because it can be used as replacement for spaces), and percent sign '%'
+ (which is used for encoding / escaping).
+
You can generate the projects list index file using the project_index
action (the 'TXT' link on projects list page) directly from gitweb.
diff --git a/gitweb/README b/gitweb/README
index 356ab7b..6908036 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -156,10 +156,11 @@ not include variables usually directly set during build):
set correctly for gitweb to find repositories.
* $projects_list
Source of projects list, either directory to scan, or text file
- with list of repositories (in the "<URI-encoded repository path> SPC
- <URI-encoded repository owner>" format). Set to $GITWEB_LIST
- during installation. If empty, $projectroot is used to scan for
- repositories.
+ with list of repositories (in the "<URI-encoded repository path> SP
+ <URI-encoded repository owner>" line format; actually there can be
+ any sequence of whitespace in place of space (SP)). Set to
+ $GITWEB_LIST during installation. If empty, $projectroot is used
+ to scan for repositories.
* $my_url, $my_uri
URL and absolute URL of gitweb script; you might need to set those
variables if you are using 'pathinfo' feature: see also below.
@@ -214,6 +215,39 @@ not include variables usually directly set during build):
('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or
set it to () if you don't want to have renames detection.
+
+Projects list file format
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Instead of having gitweb find repositories by scanning filesystem starting
+from $projectroot (or $projects_list, if it points to directory), you can
+provide list of projects by setting $projects_list to a text file with list
+of projects (and some additional info). This file uses the following
+format:
+
+One record (for project / repository) per line, whitespace separated fields;
+does not support (at least for now) lines continuation (newline escaping).
+Leading and trailing whitespace are ignored, any run of whitespace can be
+used as field separator (rules for Perl's "split(' ', $line)"). Keyed by
+the first field, which is project name, i.e. path to repository GIT_DIR
+relative to $projectroot. Fields use modified URI encoding, defined in
+RFC 3986, section 2.1 (Percent-Encoding), or rather "Query string encoding"
+(see http://en.wikipedia.org/wiki/Query_string#URL_encoding), the difference
+being that SP (' ') can be encoded as '+' (and therefore '+' has to be also
+percent-encoded). Reserved characters are: '%' (used for encoding), '+'
+(can be used to encode SPACE), all whitespace characters as defined in Perl,
+including SP, TAB and LF, (used to separate fields in a record).
+
+Currently list of fields is
+ * <repository path> - path to repository GIT_DIR, relative to $projectroot
+ * <repository owner> - displayed as repository owner, preferably full name,
+ or email, or both
+
+You can additionally use $projects_list file to limit which repositories
+are visible, and together with $strict_export to limit access to
+repositories (see "Gitweb repositories" section in gitweb/INSTALL).
+
+
Per-repository gitweb configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -225,8 +259,8 @@ You can use the following files in repository:
* README.html
A .html file (HTML fragment) which is included on the gitweb project
summary page inside <div> block element. You can use it for longer
- description of a project, to provide links for example to projects
- homepage, etc.
+ description of a project, to provide links (for example to project's
+ homepage), etc.
* description (or gitweb.description)
Short (shortened by default to 25 characters in the projects list page)
single line description of a project (of a repository). Plain text file;
^ permalink raw reply related
* Re: [FIXED PATCH] Make rebase save ORIG_HEAD if changing current branch
From: Junio C Hamano @ 2008-07-07 21:58 UTC (permalink / raw)
To: Git List; +Cc: Brian Gernhardt
In-Reply-To: <7vbq1a8ay3.fsf@gitster.siamese.dyndns.org>
Junio C Hamano <gitster@pobox.com> writes:
> The idea behind ORIG_HEAD is to have an anchoring point before an
> operation that moves your HEAD in a drastic way. Think if it as a
> poor-man's reflog -- in fact it predates reflog.
>
> That is why reset saves away the HEAD before it does its thing, so that
> you can easily say "Oops, I did not mean it -- reset ORIG_HEAD" to flip
> back to the previous state. Both a fast-forward merge and a real merge
> can be undone by resetting back to ORIG_HEAD.
I've also seen people complain (quite rightfully) that these FOO_HEAD
pseudo refs are not documented in a central place.
How about doing this? It should make it clear what ORIG_HEAD is meant to
record, while describing others.
And to answer your "git rebase --onto this from that-branch" question, I
think ORIG_HEAD should record the tip of that-branch before rebase takes
place, not the commit you happened to be at before running it. Switching
branch to that-branch is not the drastic and unforseeable part. The
drastic and unforseeable change is rebasing and seeing that the rebased
result does not work with the new upstream `from`, and the user would want
to have a way to quickly rewind the tip of the branch back to the state
before the rebase. The new paragraph added by this patch should hopefully
make this reasoning more clear.
-- >8 --
Documentation: update sections on naming revisions and revision ranges
Various *_HEAD pseudo refs were not documented in any central place.
Especially since we may be teaching rebase and am to record ORIG_HEAD,
it would be a good time to do so.
While at it, reword the explanation on r1..r2 notation to reduce
confusion.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Documentation/git-rev-parse.txt | 20 +++++++++++++++-----
1 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 378a312..7184274 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -166,7 +166,7 @@ blobs contained in a commit.
first match in the following rules:
. if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
- useful only for `HEAD`, `FETCH_HEAD` and `MERGE_HEAD`);
+ useful only for `HEAD`, `FETCH_HEAD`, `ORIG_HEAD` and `MERGE_HEAD`);
. otherwise, `$GIT_DIR/refs/<name>` if exists;
@@ -177,6 +177,16 @@ blobs contained in a commit.
. otherwise, `$GIT_DIR/refs/remotes/<name>` if exists;
. otherwise, `$GIT_DIR/refs/remotes/<name>/HEAD` if exists.
++
+HEAD names the commit your changes in the working tree is based on.
+FETCH_HEAD records the branch you fetched from a remote repository
+with your last 'git-fetch' invocation.
+ORIG_HEAD is created by commands that moves your HEAD in a drastic
+way, to record the position of the HEAD before their operation, so that
+you can change the tip of the branch back to the state before you ran
+them easily.
+MERGE_HEAD records the commit(s) you are merging into your branch
+when you run 'git-merge'.
* A ref followed by the suffix '@' with a date specification
enclosed in a brace
@@ -289,10 +299,10 @@ notation is used. E.g. "`{caret}r1 r2`" means commits reachable
from `r2` but exclude the ones reachable from `r1`.
This set operation appears so often that there is a shorthand
-for it. "`r1..r2`" is equivalent to "`{caret}r1 r2`". It is
-the difference of two sets (subtract the set of commits
-reachable from `r1` from the set of commits reachable from
-`r2`).
+for it. When you have two commits `r1` and `r2` (named according
+to the syntax explained in SPECIFYING REVISIONS above), you can ask
+for commits that are reachable from r2 but not from r1 by
+"`{caret}r1 r2`" and it can be written as "`r1..r2`".
A similar notation "`r1\...r2`" is called symmetric difference
of `r1` and `r2` and is defined as
^ permalink raw reply related
* Cloning marks pack for .keep
From: Jean-Luc Herren @ 2008-07-07 21:27 UTC (permalink / raw)
To: Git Mailing List; +Cc: Shawn O. Pearce
After cloning a local repository with "git clone file://...", the
resulting repo had one big pack file, as expected, but also a
matching ".keep" file. Certainly this is a bug, isn't it? The
same happens if I clone git.git. I used git 1.5.6.1 but observed
the same with the current master. I bisected this behavior to
commit fa740529 by Shawn O. Pearce (CC'ing him). Since this dates
back to 2007, I wonder if maybe only I am seeing this, but I
cannot think of any reason for it.
(I already mentioned this on IRC today.)
jlh
^ permalink raw reply
* Re: [StGit PATCH 0/2] push optimizations
From: Catalin Marinas @ 2008-07-07 21:12 UTC (permalink / raw)
To: Karl Hasselström; +Cc: git
In-Reply-To: <20080702060113.11361.39006.stgit@yoghurt>
2008/7/2 Karl Hasselström <kha@treskal.com>:
> Here's the git-apply call you asked for. You were right: it was a huge
> speed-up.
I know, I've been through this couple of years ago :-)
> I set up a benchmark to test it:
>
> * 32 directories, each containing 32 subdirectories, each containing
> 32 small (and different) files.
Can you try with a Linux kernel like the -mm tree? You get normally
sized patches which might show a difference with the patch log. You
can clone the for-akpm branch on git://linux-arm.org/linux-2.6 and
just uncommit ~300 patches.
> * I set all this up with a python script feeding fast-import. A huge
> time-saver!
What is fast-import?
>
> * Pop patches, git-reset to upstream, then goto top patch. This
> makes sure that we use the new infrastructure to push, and that we
> get one file-level conflict in each patch.
>
> Before the first patch, the "goto" command took 4:27 minutes,
> wall-clock time. After the first patch, it took 1:31. After the
> second, 0:48; one second or so slower than the stable branch (which
> does not have a patch stack log).
One second is just noise and depends on how warm the caches are. You
could run a few times consecutively and discard the first result but
we don't need to be that accurate.
--
Catalin
^ permalink raw reply
* [PATCH] Catch failures from t5540-http-push
From: Mike Hommey @ 2008-07-07 21:06 UTC (permalink / raw)
To: git, gitster
In-Reply-To: <7vej6531xa.fsf@gitster.siamese.dyndns.org>
git http-push doesn't handle packed-refs, and now the new builtin-clone
created packed refs, the http-push test fails.
Mark the current failure as such, and also catch third test's failure
that went unreported because git push doesn't return an error code when
it says:
No refs in common and none specified; doing nothing.
Which it does when http-push can't get a list of refs recursively from
$URL/refs/.
Signed-off-by: Mike Hommey <mh@glandium.org>
---
Feel free to squash this in the previous one, if you feel it's better.
t/t5540-http-push.sh | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index 147ff98..21dbb55 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -51,16 +51,17 @@ test_expect_success 'clone remote repository' '
git clone $HTTPD_URL/test_repo.git test_repo_clone
'
-test_expect_success 'push to remote repository' '
+test_expect_failure 'push to remote repository' '
cd "$ROOT_PATH"/test_repo_clone &&
: >path2 &&
git add path2 &&
test_tick &&
git commit -m path2 &&
- git push
+ git push &&
+ [ -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/refs/heads/master" ]
'
-test_expect_success 'create and delete remote branch' '
+test_expect_failure 'create and delete remote branch' '
cd "$ROOT_PATH"/test_repo_clone &&
git checkout -b dev &&
: >path3 &&
--
1.5.6.GIT
^ permalink raw reply related
* Re: [StGit PATCH 2/2] Implement "stg refresh --edit" again
From: Catalin Marinas @ 2008-07-07 20:56 UTC (permalink / raw)
To: Karl Hasselström; +Cc: git, Jakub Narebski
In-Reply-To: <20080704064036.9637.52951.stgit@yoghurt>
2008/7/4 Karl Hasselström <kha@treskal.com>:
> The -e/--edit flag to "stg refresh" was dropped between v0.13 and
> v0.14, causing severe user dissatisfaction. This patch restores it,
> along with -m/--message, -f/--file, --sign, --ack, --author,
> --authname, --authemail, and --authdate.
Thanks.
> I omitted the --committer options on purpose; I think they are a
> mistake. Falsifying the committer info is not a common operation, and
> if one wishes to do it for some reason, one can always set the
> GIT_COMMITTER_* environment variables.
I agree.
--
Catalin
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox