* [PATCH] "checkout-cache -m" writes unmerged contents for each stage.
@ 2005-04-17 23:03 Junio C Hamano
2005-04-18 2:05 ` Linus Torvalds
0 siblings, 1 reply; 6+ messages in thread
From: Junio C Hamano @ 2005-04-17 23:03 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
This is the alternative strategy I mentioned in my previous
message. A new -m option to checkout-cache causes it to store
contents of unmerged paths in path~1~, path~2~, and path~3~.
To be applied on top of the previous patch I re-sent:
[PATCH] checkout-cache -a should not extract unmerged stages.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
checkout-cache.c | 93 ++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 76 insertions(+), 17 deletions(-)
checkout-cache.c: 596471a2f98b80a622488cf04edf9e95ce8666b1
--- checkout-cache.c
+++ checkout-cache.c 2005-04-17 15:49:15.000000000 -0700
@@ -34,7 +34,7 @@
*/
#include "cache.h"
-static int force = 0, quiet = 0;
+static int force = 0, merge = 0, quiet = 0;
static void create_directories(const char *path)
{
@@ -65,6 +65,31 @@ static int create_file(const char *path,
return fd;
}
+/* Returns the pathname itself for a merged entry
+ * and pathname~N~ for an unmerged one.
+ * Do not free the value you get from this function.
+ */
+static char *ce_name_with_stage(struct cache_entry *ce)
+{
+ /* (CE_NAMEMASK+1) is the max length of a name.
+ * We are adding 3 bytes for ~N~ and we need a terminating NUL
+ * hence +5.
+ */
+ static char name[CE_NAMEMASK + 5];
+ int stage = ce_stage(ce);
+ if (! stage)
+ return ce->name;
+ else {
+ int pos = ce_namelen(ce);
+ strcpy(name, ce->name);
+ name[pos++] = '~';
+ name[pos++] = '0' + stage;
+ name[pos++] = '~';
+ name[pos] = 0;
+ return name;
+ }
+}
+
static int write_entry(struct cache_entry *ce)
{
int fd;
@@ -72,13 +97,20 @@ static int write_entry(struct cache_entr
unsigned long size;
long wrote;
char type[20];
+ char *name;
new = read_sha1_file(ce->sha1, type, &size);
if (!new || strcmp(type, "blob")) {
return error("checkout-cache: unable to read sha1 file of %s (%s)",
ce->name, sha1_to_hex(ce->sha1));
}
- fd = create_file(ce->name, ntohl(ce->ce_mode));
+ name = ce_name_with_stage(ce);
+ if (!quiet && name != ce->name)
+ fprintf(stderr,
+ "checkout-cache: storing stage %d of %s in %s\n",
+ ce_stage(ce), ce->name, name);
+
+ fd = create_file(name, ntohl(ce->ce_mode));
if (fd < 0) {
free(new);
return error("checkout-cache: unable to create %s (%s)",
@@ -117,19 +149,39 @@ static int checkout_entry(struct cache_e
return write_entry(ce);
}
+static int checkout_unmerged(int pos)
+{
+ int i, err;
+ struct cache_entry *ce = active_cache[pos];
+
+ for (err = 0, i = pos;
+ (i < active_nr &&
+ !strcmp(active_cache[i]->name, ce->name));
+ i++)
+ err |= checkout_entry(active_cache[i]);
+ return err;
+}
+
static int checkout_file(const char *name)
{
int pos = cache_name_pos(name, strlen(name));
if (pos < 0) {
- if (!quiet) {
- pos = -pos - 1;
- fprintf(stderr,
- "checkout-cache: %s is %s.\n",
- name,
- (pos < active_nr &&
- !strcmp(active_cache[pos]->name, name)) ?
- "unmerged" : "not in the cache");
+ pos = -pos - 1;
+ if (pos < active_nr &&
+ !strcmp(active_cache[pos]->name, name)) {
+ if (merge)
+ return checkout_unmerged(pos);
+ else if (! quiet) {
+ fprintf(stderr,
+ "checkout-cache: %s is unmerged.\n",
+ name);
+ return -1;
+ }
}
+ else if (! quiet)
+ fprintf(stderr,
+ "checkout-cache: %s is not in the cache.\n",
+ name);
return -1;
}
return checkout_entry(active_cache[pos]);
@@ -137,22 +189,25 @@ static int checkout_file(const char *nam
static int checkout_all(void)
{
- struct cache_entry *unmerge_skipping = NULL;
int i;
for (i = 0; i < active_nr ; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) {
- if (!unmerge_skipping ||
- strcmp(unmerge_skipping->name, ce->name))
+ if (!merge)
fprintf(stderr,
"checkout-cache: needs merge %s\n",
ce->name);
- unmerge_skipping = ce;
- continue;
+ while (i < active_nr &&
+ !strcmp(ce->name, active_cache[i]->name)) {
+ if (merge) {
+ checkout_entry(active_cache[i]);
+ }
+ i++;
+ }
+ i--;
}
- unmerge_skipping = NULL;
- if (checkout_entry(ce) < 0)
+ else if (checkout_entry(ce) < 0)
return -1;
}
return 0;
@@ -181,6 +236,10 @@ int main(int argc, char **argv)
force = 1;
continue;
}
+ if (!strcmp(arg, "-m")) {
+ merge = 1;
+ continue;
+ }
if (!strcmp(arg, "-q")) {
quiet = 1;
continue;
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] "checkout-cache -m" writes unmerged contents for each stage.
2005-04-17 23:03 [PATCH] "checkout-cache -m" writes unmerged contents for each stage Junio C Hamano
@ 2005-04-18 2:05 ` Linus Torvalds
2005-04-18 2:19 ` Junio C Hamano
2005-04-18 2:44 ` Junio C Hamano
0 siblings, 2 replies; 6+ messages in thread
From: Linus Torvalds @ 2005-04-18 2:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Sun, 17 Apr 2005, Junio C Hamano wrote:
>
> This is the alternative strategy I mentioned in my previous
> message. A new -m option to checkout-cache causes it to store
> contents of unmerged paths in path~1~, path~2~, and path~3~.
I don't think this is wrong, but it all seems a bit hacky. The reason I
didn't apply your other patch was that I just don't feel quite comfortable
with what is the RightThing(tm) to do yet.
I'm actually fairly certain that the right thing to do is to silently
ignore all unmerged entries. They just aren't "interesting" from a
checkout-cache perspective, least of all when given the "-a" flag. So even
warning about them seems to be pointless.
The other issue is that obviously we _do_ need to have some way to merge
them, but as far as I can tell, we've really got that already in
"show-files --unmerged" together with "cat-file blob xxxx". I do believe
that it's a damn ugly script, and that maybe we could create some helper
plumbing to make it more obvious, but I haven't quite decided what the
right helper interface would be.
Writing files to the current working directory I don't like, especially as
we don't even necessarily write all three files (so with your example
code, if there was a left-over xxx~1~ file from a previous merge, your
merger would really get confused now since it thinks it has an original
file that didn't actually really exist). So I'd much rather write them to
a /tmp directory instead, let the user merge them some way.
I'm actually thinking that maybe the _right_ interface is to do something
like this:
merge-cache <program> <filename>
and what that does is to look up the <filename> in the cache, and if it
has any merge entries, unpack all of them (which may be just one file, of
course) into up to three separate files (mkstemp()), and then execve the
supplied program name with those three files as arguments 1,2,3 (empty
argument if no file), and "filename" as argument 4.
So now the program could be a simple script:
#!/bin/sh
#
# This is the git merge script, called with
#
# $1 - original file (or empty string)
# $2 - file in branch1 (or empty string)
# $3 - file in branch2 (or empty string)
# $4 - pathname in repository
#
#
# Case 1: file removed in both
#
if [ -z "$2" && -z "$3" ]; then
rm -- "$4"
update-cache --remove -- "$4"
exit 0
fi
#
# Case 2: file exists in just one
#
if [ -z "$2" || -z "$3" ] then
cat "$2""$3" > "$4"
update-cache --add -- "$4"
exit 0
fi
#
# Case 3: file exists in both
#
src="$1"
if [ -z "$1" ]; then
src=/den/null
fi
merge "$3" "$src" "$2" && cp "$3" "$4" && update-cache --add -- "$4"
or you could so something fancier.
This would _seem_ to be a very simple way to generate a nice merge (the
merge script can obviously be a lot smarter than the above thing,
including doing nice graphical merge interfaces).
An extension of this might be to make "merge-cache" take several
filenames, and merge them one at a time (stopping if the merge program
returns a failure code). And obviously this also expands trivially to the
normal "-a" case, which would do the same, except it would just call the
merge program for any file it finds that needs merging)
What do you think? I can whip up a "merge-cache" program like that in five
minutes, and it _seems_ like the right interface..
Linus
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] "checkout-cache -m" writes unmerged contents for each stage.
2005-04-18 2:05 ` Linus Torvalds
@ 2005-04-18 2:19 ` Junio C Hamano
2005-04-18 3:05 ` Linus Torvalds
2005-04-18 2:44 ` Junio C Hamano
1 sibling, 1 reply; 6+ messages in thread
From: Junio C Hamano @ 2005-04-18 2:19 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> On Sun, 17 Apr 2005, Junio C Hamano wrote:
>>
>> This is the alternative strategy I mentioned in my previous
>> message. A new -m option to checkout-cache causes it to store
>> contents of unmerged paths in path~1~, path~2~, and path~3~.
LT> I don't think this is wrong, but it all seems a bit hacky. The reason I
LT> didn't apply your other patch was that I just don't feel quite comfortable
LT> with what is the RightThing(tm) to do yet.
I do not personally like that '-m' thing either so please feel
free to drop it. I was just toying with the idea. I knew you
would not use "-m" yourself (and I would not myself either).
The patch was primarily meant for Pasky to raise his opinion.
If having something like this helps Cogito then he can keep it.
If Cogito will be doing the "show-files --unmerged" with
"cat-file blob" itself and this "checkout-cache -m" does not
help it that much then there is no point having this hack.
LT> I'm actually fairly certain that the right thing to do is to silently
LT> ignore all unmerged entries. They just aren't "interesting" from a
LT> checkout-cache perspective, least of all when given the "-a" flag. So even
LT> warning about them seems to be pointless.
I do not mind your dropping the "warning" bit, but please keep
the skipping bit for "-a". Dropping that TT from stage 1 into
the working directory and then complaining that TT exists when
it then tries to extract stage 2 and stage 3 is simply madness.
LT> What do you think? I can whip up a "merge-cache" program like that in five
LT> minutes, and it _seems_ like the right interface..
Yes. I think that is the right thing to do. In fact the idea
is quite similar to what I've been working on, which is a
rewrite of that perl thing to use "read-tree -m O A B".
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] "checkout-cache -m" writes unmerged contents for each stage.
2005-04-18 2:19 ` Junio C Hamano
@ 2005-04-18 3:05 ` Linus Torvalds
0 siblings, 0 replies; 6+ messages in thread
From: Linus Torvalds @ 2005-04-18 3:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Sun, 17 Apr 2005, Junio C Hamano wrote:
>
> LT> What do you think? I can whip up a "merge-cache" program like that in five
> LT> minutes, and it _seems_ like the right interface..
>
> Yes. I think that is the right thing to do. In fact the idea
> is quite similar to what I've been working on, which is a
> rewrite of that perl thing to use "read-tree -m O A B".
I pushed the thing out. It was indeed pretty trivial.
Just as an example, on your merge-test, I can then do
merge-cache echo -a
and the output is
.merge_file_Raanu4 .merge_file_q7ZoLY AA
.merge_file_zMXLAW .merge_file_SOugrU AN
.merge_file_tWf5zS DD
.merge_file_YmGzgR DM
.merge_file_N1M8oO DN
.merge_file_eAC5WL .merge_file_ROg7mM MD
.merge_file_G1AWSM .merge_file_LMCruN .merge_file_ucVx7N MM
.merge_file_3YxD2O NA
.merge_file_2SJa6P .merge_file_3V5g6Q ND
.merge_file_SIo4nQ S/DD
.merge_file_dO8AOP S/DM
.merge_file_wOO6iP S/DN
.merge_file_RrgnYR .merge_file_KQxqHU S/MD
.merge_file_F0sQRX .merge_file_E4DC00 .merge_file_HQkyn2 S/MM
.merge_file_od0mf4 .merge_file_Xvv035 S/ND
.merge_file_oEtq17 .merge_file_TwkT5c .merge_file_aqx58h Trivial
(Note that the spaces signify an empty argument in those places, since the
file in question didn't always exist). It executed the "echo" thing 16
times (once for each object that your script-from-hell had caused a clash
with).
In all cases you have $1 being "original", $2 being "branch 1", $3 being
"branch 2", and $4 being "name in cache".
ALERT ALERT ALERT! The git "merge object order" is different from the
"merge" program merge object order. In the above ordering, the original is
first. But the argument order to the 3-way merge program "merge" is to
have the original in the middle. Don't ask me why.
Anyway, another example:
torvalds@ppc970:~/merge-test> merge-cache cat MM
This is MM from the original tree. # original
This is modified MM in the branch A. # merge1
This is modified MM in the branch B. # merge2
This is modified MM in the branch B. # current contents
or
torvalds@ppc970:~/merge-test> merge-cache cat AA MM
cat: : No such file or directory
This is added AA in the branch A.
This is added AA in the branch B.
This is added AA in the branch B.
fatal: merge program failed
where the latter example shows how "merge-cache" will stop trying to merge
once anything has returned an error (ie "cat" returned an error for the AA
file, because it didn't exist in the original, and thus "merge-cache"
didn't even try to merge the MM thing).
Linus
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] "checkout-cache -m" writes unmerged contents for each stage.
2005-04-18 2:05 ` Linus Torvalds
2005-04-18 2:19 ` Junio C Hamano
@ 2005-04-18 2:44 ` Junio C Hamano
2005-04-18 2:56 ` Linus Torvalds
1 sibling, 1 reply; 6+ messages in thread
From: Junio C Hamano @ 2005-04-18 2:44 UTC (permalink / raw)
To: Linus Torvalds; +Cc: git
>>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes:
LT> I'm actually thinking that maybe the _right_ interface is to do something
LT> like this:
LT> merge-cache <program> <filename>
LT> and what that does is to look up the <filename> in the cache, and if it
LT> has any merge entries, unpack all of them (which may be just one file, of
LT> course) into up to three separate files (mkstemp()), and then execve the
LT> supplied program name with those three files as arguments 1,2,3 (empty
LT> argument if no file), and "filename" as argument 4.
LT> What do you think? I can whip up a "merge-cache" program like that in five
LT> minutes, and it _seems_ like the right interface..
One small detail. What about the "-x" bit?
In case 2 and 3 in your sample merge script, the "merge script"
needs to know what the preferred mode bits are before running
"update-cache --add".
Yes, it can figure that out by running "show-files --unmerged"
and grepping for "$4" by itself, but then it can figure out the
information in "$1" through "$3" by itself as well, so that
makes having merge-cache wrapper less useful to begin with.
I do not think it is realistic for these three related trees to
have files that differ in -x bit, so it would not be that useful
to give the "merge script" the flexibility to pick -x bit value
among three parents --- it would be fine for the merge-cache
wrapper to dictate the value of the -x bit for the resulting
file. So I'd suggest to add an extra parameter to the "merge
script" when merge-cache wrapper calls it.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] "checkout-cache -m" writes unmerged contents for each stage.
2005-04-18 2:44 ` Junio C Hamano
@ 2005-04-18 2:56 ` Linus Torvalds
0 siblings, 0 replies; 6+ messages in thread
From: Linus Torvalds @ 2005-04-18 2:56 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On Sun, 17 Apr 2005, Junio C Hamano wrote:
>
> One small detail. What about the "-x" bit?
You'll need to merge those kinds of changes separately for now. We'll have
to resolve that too, right now I think the -x bit gets lost on a merge
simply because we just write the temp-files, something merges them, and
does an "update-cache" on the merged result, and we'll pick up the -x bit
(or rather, lack of one) at that point.
Linus
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2005-04-18 2:59 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-04-17 23:03 [PATCH] "checkout-cache -m" writes unmerged contents for each stage Junio C Hamano
2005-04-18 2:05 ` Linus Torvalds
2005-04-18 2:19 ` Junio C Hamano
2005-04-18 3:05 ` Linus Torvalds
2005-04-18 2:44 ` Junio C Hamano
2005-04-18 2:56 ` Linus Torvalds
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox