summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-checkout-index.txt51
-rw-r--r--Makefile4
-rw-r--r--apply.c3
-rw-r--r--cache.h2
-rw-r--r--checkout-index.c86
-rw-r--r--count-delta.c72
-rw-r--r--count-delta.h10
-rw-r--r--diffcore-break.c45
-rw-r--r--diffcore-delta.c96
-rw-r--r--diffcore-rename.c20
-rw-r--r--diffcore.h4
-rw-r--r--entry.c42
-rw-r--r--read-tree.c2
-rwxr-xr-xt/t2004-checkout-cache-temp.sh212
14 files changed, 477 insertions, 172 deletions
diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt
index b0b65889ac..09bd6a5535 100644
--- a/Documentation/git-checkout-index.txt
+++ b/Documentation/git-checkout-index.txt
@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git-checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>]
- [--stage=<number>]
+ [--stage=<number>|all]
+ [--temp]
[-z] [--stdin]
[--] [<file>]\*
@@ -43,9 +44,15 @@ OPTIONS
When creating files, prepend <string> (usually a directory
including a trailing /)
---stage=<number>::
+--stage=<number>|all::
Instead of checking out unmerged entries, copy out the
files from named stage. <number> must be between 1 and 3.
+ Note: --stage=all automatically implies --temp.
+
+--temp::
+ Instead of copying the files to the working directory
+ write the content to temporary files. The temporary name
+ associations will be written to stdout.
--stdin::
Instead of taking list of paths from the command line,
@@ -87,6 +94,46 @@ it will prevent problems with a filename of, for example, `-a`.
Using `--` is probably a good policy in scripts.
+Using --temp or --stage=all
+---------------------------
+When `--temp` is used (or implied by `--stage=all`)
+`git-checkout-index` will create a temporary file for each index
+entry being checked out. The index will not be updated with stat
+information. These options can be useful if the caller needs all
+stages of all unmerged entries so that the unmerged files can be
+processed by an external merge tool.
+
+A listing will be written to stdout providing the association of
+temporary file names to tracked path names. The listing format
+has two variations:
+
+ . tempname TAB path RS
++
+The first format is what gets used when `--stage` is omitted or
+is not `--stage=all`. The field tempname is the temporary file
+name holding the file content and path is the tracked path name in
+the index. Only the requested entries are output.
+
+ . stage1temp SP stage2temp SP stage3tmp TAB path RS
++
+The second format is what gets used when `--stage=all`. The three
+stage temporary fields (stage1temp, stage2temp, stage3temp) list the
+name of the temporary file if there is a stage entry in the index
+or `.` if there is no stage entry. Paths which only have a stage 0
+entry will always be omitted from the output.
+
+In both formats RS (the record separator) is newline by default
+but will be the null byte if -z was passed on the command line.
+The temporary file names are always safe strings; they will never
+contain directory separators or whitespace characters. The path
+field is always relative to the current directory and the temporary
+file names are always relative to the top level directory.
+
+If the object being copied out to a temporary file is a symbolic
+link the content of the link will be written to a normal file. It is
+up to the end-user or the Porcelain to make use of this information.
+
+
EXAMPLES
--------
To update and refresh only the files already checked out::
diff --git a/Makefile b/Makefile
index b6d8804d4b..ab2890d638 100644
--- a/Makefile
+++ b/Makefile
@@ -190,7 +190,7 @@ PYMODULES = \
LIB_FILE=libgit.a
LIB_H = \
- blob.h cache.h commit.h count-delta.h csum-file.h delta.h \
+ blob.h cache.h commit.h csum-file.h delta.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h
@@ -200,7 +200,7 @@ DIFF_OBJS = \
diffcore-delta.o
LIB_OBJS = \
- blob.o commit.o connect.o count-delta.o csum-file.o \
+ blob.o commit.o connect.o csum-file.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
diff --git a/apply.c b/apply.c
index c369966867..849a8b4485 100644
--- a/apply.c
+++ b/apply.c
@@ -1402,7 +1402,8 @@ static int check_patch(struct patch *patch)
costate.not_new = 0;
costate.refresh_cache = 1;
if (checkout_entry(active_cache[pos],
- &costate) ||
+ &costate,
+ NULL) ||
lstat(old_name, &st))
return -1;
}
diff --git a/cache.h b/cache.h
index 8dc1de16e4..1f962809b0 100644
--- a/cache.h
+++ b/cache.h
@@ -262,7 +262,7 @@ struct checkout {
refresh_cache:1;
};
-extern int checkout_entry(struct cache_entry *ce, struct checkout *state);
+extern int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath);
extern struct alternate_object_database {
struct alternate_object_database *next;
diff --git a/checkout-index.c b/checkout-index.c
index f54c606414..7b78715417 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -40,9 +40,13 @@
#include "strbuf.h"
#include "quote.h"
+#define CHECKOUT_ALL 4
static const char *prefix;
static int prefix_length;
+static int line_termination = '\n';
static int checkout_stage; /* default to checkout stage0 */
+static int to_tempfile;
+static char topath[4][MAXPATHLEN+1];
static struct checkout state = {
.base_dir = "",
@@ -53,11 +57,39 @@ static struct checkout state = {
.refresh_cache = 0,
};
+static void write_tempfile_record (const char *name)
+{
+ int i;
+
+ if (CHECKOUT_ALL == checkout_stage) {
+ for (i = 1; i < 4; i++) {
+ if (i > 1)
+ putchar(' ');
+ if (topath[i][0])
+ fputs(topath[i], stdout);
+ else
+ putchar('.');
+ }
+ } else
+ fputs(topath[checkout_stage], stdout);
+
+ putchar('\t');
+ write_name_quoted("", 0, name + prefix_length,
+ line_termination, stdout);
+ putchar(line_termination);
+
+ for (i = 0; i < 4; i++) {
+ topath[i][0] = 0;
+ }
+}
+
static int checkout_file(const char *name)
{
int namelen = strlen(name);
int pos = cache_name_pos(name, namelen);
int has_same_name = 0;
+ int did_checkout = 0;
+ int errs = 0;
if (pos < 0)
pos = -pos - 1;
@@ -68,9 +100,20 @@ static int checkout_file(const char *name)
memcmp(ce->name, name, namelen))
break;
has_same_name = 1;
- if (checkout_stage == ce_stage(ce))
- return checkout_entry(ce, &state);
pos++;
+ if (ce_stage(ce) != checkout_stage
+ && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
+ continue;
+ did_checkout = 1;
+ if (checkout_entry(ce, &state,
+ to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
+ errs++;
+ }
+
+ if (did_checkout) {
+ if (to_tempfile)
+ write_tempfile_record(name);
+ return errs > 0 ? -1 : 0;
}
if (!state.quiet) {
@@ -90,18 +133,29 @@ static int checkout_file(const char *name)
static int checkout_all(void)
{
int i, errs = 0;
+ struct cache_entry* last_ce = 0;
for (i = 0; i < active_nr ; i++) {
struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce) != checkout_stage)
+ if (ce_stage(ce) != checkout_stage
+ && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce)))
continue;
if (prefix && *prefix &&
(ce_namelen(ce) <= prefix_length ||
memcmp(prefix, ce->name, prefix_length)))
continue;
- if (checkout_entry(ce, &state) < 0)
+ if (last_ce && to_tempfile) {
+ if (ce_namelen(last_ce) != ce_namelen(ce)
+ || memcmp(last_ce->name, ce->name, ce_namelen(ce)))
+ write_tempfile_record(last_ce->name);
+ }
+ if (checkout_entry(ce, &state,
+ to_tempfile ? topath[ce_stage(ce)] : NULL) < 0)
errs++;
+ last_ce = ce;
}
+ if (last_ce && to_tempfile)
+ write_tempfile_record(last_ce->name);
if (errs)
/* we have already done our error reporting.
* exit with the same code as die().
@@ -111,7 +165,7 @@ static int checkout_all(void)
}
static const char checkout_cache_usage[] =
-"git-checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]] [--prefix=<string>] [--] <file>...";
+"git-checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]|all] [--prefix=<string>] [--temp] [--] <file>...";
static struct cache_file cache_file;
@@ -121,7 +175,6 @@ int main(int argc, char **argv)
int newfd = -1;
int all = 0;
int read_from_stdin = 0;
- int line_termination = '\n';
prefix = setup_git_directory();
git_config(git_default_config);
@@ -175,17 +228,26 @@ int main(int argc, char **argv)
i++; /* do not consider arg as a file name */
break;
}
+ if (!strcmp(arg, "--temp")) {
+ to_tempfile = 1;
+ continue;
+ }
if (!strncmp(arg, "--prefix=", 9)) {
state.base_dir = arg+9;
state.base_dir_len = strlen(state.base_dir);
continue;
}
if (!strncmp(arg, "--stage=", 8)) {
- int ch = arg[8];
- if ('1' <= ch && ch <= '3')
- checkout_stage = arg[8] - '0';
- else
- die("stage should be between 1 and 3");
+ if (!strcmp(arg + 8, "all")) {
+ to_tempfile = 1;
+ checkout_stage = CHECKOUT_ALL;
+ } else {
+ int ch = arg[8];
+ if ('1' <= ch && ch <= '3')
+ checkout_stage = arg[8] - '0';
+ else
+ die("stage should be between 1 and 3 or all");
+ }
continue;
}
if (arg[0] == '-')
@@ -193,7 +255,7 @@ int main(int argc, char **argv)
break;
}
- if (state.base_dir_len) {
+ if (state.base_dir_len || to_tempfile) {
/* when --prefix is specified we do not
* want to update cache.
*/
diff --git a/count-delta.c b/count-delta.c
deleted file mode 100644
index 058a2aadb1..0000000000
--- a/count-delta.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2005 Junio C Hamano
- * The delta-parsing part is almost straight copy of patch-delta.c
- * which is (C) 2005 Nicolas Pitre <nico@cam.org>.
- */
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include "delta.h"
-#include "count-delta.h"
-
-/*
- * NOTE. We do not _interpret_ delta fully. As an approximation, we
- * just count the number of bytes that are copied from the source, and
- * the number of literal data bytes that are inserted.
- *
- * Number of bytes that are _not_ copied from the source is deletion,
- * and number of inserted literal bytes are addition, so sum of them
- * is the extent of damage.
- */
-int count_delta(void *delta_buf, unsigned long delta_size,
- unsigned long *src_copied, unsigned long *literal_added)
-{
- unsigned long copied_from_source, added_literal;
- const unsigned char *data, *top;
- unsigned char cmd;
- unsigned long src_size, dst_size, out;
-
- if (delta_size < DELTA_SIZE_MIN)
- return -1;
-
- data = delta_buf;
- top = delta_buf + delta_size;
-
- src_size = get_delta_hdr_size(&data);
- dst_size = get_delta_hdr_size(&data);
-
- added_literal = copied_from_source = out = 0;
- while (data < top) {
- cmd = *data++;
- if (cmd & 0x80) {
- unsigned long cp_off = 0, cp_size = 0;
- if (cmd & 0x01) cp_off = *data++;
- if (cmd & 0x02) cp_off |= (*data++ << 8);
- if (cmd & 0x04) cp_off |= (*data++ << 16);
- if (cmd & 0x08) cp_off |= (*data++ << 24);
- if (cmd & 0x10) cp_size = *data++;
- if (cmd & 0x20) cp_size |= (*data++ << 8);
- if (cmd & 0x40) cp_size |= (*data++ << 16);
- if (cp_size == 0) cp_size = 0x10000;
-
- copied_from_source += cp_size;
- out += cp_size;
- } else {
- /* write literal into dst */
- added_literal += cmd;
- out += cmd;
- data += cmd;
- }
- }
-
- /* sanity check */
- if (data != top || out != dst_size)
- return -1;
-
- /* delete size is what was _not_ copied from source.
- * edit size is that and literal additions.
- */
- *src_copied = copied_from_source;
- *literal_added = added_literal;
- return 0;
-}
diff --git a/count-delta.h b/count-delta.h
deleted file mode 100644
index 7359629827..0000000000
--- a/count-delta.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright (C) 2005 Junio C Hamano
- */
-#ifndef COUNT_DELTA_H
-#define COUNT_DELTA_H
-
-int count_delta(void *, unsigned long,
- unsigned long *src_copied, unsigned long *literal_added);
-
-#endif
diff --git a/diffcore-break.c b/diffcore-break.c
index 0fc2b860be..71ad58a25a 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -45,8 +45,8 @@ static int should_break(struct diff_filespec *src,
* The value we return is 1 if we want the pair to be broken,
* or 0 if we do not.
*/
- unsigned long delta_size, base_size, src_copied, literal_added;
- int to_break = 0;
+ unsigned long delta_size, base_size, src_copied, literal_added,
+ src_removed;
*merge_score_p = 0; /* assume no deletion --- "do not break"
* is the default.
@@ -72,33 +72,40 @@ static int should_break(struct diff_filespec *src,
&src_copied, &literal_added))
return 0;
+ /* sanity */
+ if (src->size < src_copied)
+ src_copied = src->size;
+ if (dst->size < literal_added + src_copied) {
+ if (src_copied < dst->size)
+ literal_added = dst->size - src_copied;
+ else
+ literal_added = 0;
+ }
+ src_removed = src->size - src_copied;
+
/* Compute merge-score, which is "how much is removed
* from the source material". The clean-up stage will
* merge the surviving pair together if the score is
* less than the minimum, after rename/copy runs.
*/
- if (src->size <= src_copied)
- ; /* all copied, nothing removed */
- else {
- delta_size = src->size - src_copied;
- *merge_score_p = delta_size * MAX_SCORE / src->size;
- }
-
+ *merge_score_p = src_removed * MAX_SCORE / src->size;
+
/* Extent of damage, which counts both inserts and
* deletes.
*/
- if (src->size + literal_added <= src_copied)
- delta_size = 0; /* avoid wrapping around */
- else
- delta_size = (src->size - src_copied) + literal_added;
-
- /* We break if the edit exceeds the minimum.
- * i.e. (break_score / MAX_SCORE < delta_size / base_size)
+ delta_size = src_removed + literal_added;
+ if (delta_size * MAX_SCORE / base_size < break_score)
+ return 0;
+
+ /* If you removed a lot without adding new material, that is
+ * not really a rewrite.
*/
- if (break_score * base_size < delta_size * MAX_SCORE)
- to_break = 1;
+ if ((src->size * break_score < src_removed * MAX_SCORE) &&
+ (literal_added * 20 < src_removed) &&
+ (literal_added * 20 < src_copied))
+ return 0;
- return to_break;
+ return 1;
}
void diffcore_break(int break_score)
diff --git a/diffcore-delta.c b/diffcore-delta.c
index 1e6a6911ec..70bacff837 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -1,32 +1,53 @@
#include "cache.h"
#include "diff.h"
#include "diffcore.h"
-#include "delta.h"
-#include "count-delta.h"
-
-static int diffcore_count_changes_1(void *src, unsigned long src_size,
- void *dst, unsigned long dst_size,
- unsigned long delta_limit,
- unsigned long *src_copied,
- unsigned long *literal_added)
+
+/*
+ * Idea here is very simple.
+ *
+ * We have total of (sz-N+1) N-byte overlapping sequences in buf whose
+ * size is sz. If the same N-byte sequence appears in both source and
+ * destination, we say the byte that starts that sequence is shared
+ * between them (i.e. copied from source to destination).
+ *
+ * For each possible N-byte sequence, if the source buffer has more
+ * instances of it than the destination buffer, that means the
+ * difference are the number of bytes not copied from source to
+ * destination. If the counts are the same, everything was copied
+ * from source to destination. If the destination has more,
+ * everything was copied, and destination added more.
+ *
+ * We are doing an approximation so we do not really have to waste
+ * memory by actually storing the sequence. We just hash them into
+ * somewhere around 2^16 hashbuckets and count the occurrences.
+ *
+ * The length of the sequence is arbitrarily set to 8 for now.
+ */
+
+#define HASHBASE 65537 /* next_prime(2^16) */
+
+static void hash_chars(unsigned char *buf, unsigned long sz, int *count)
{
- void *delta;
- unsigned long delta_size;
-
- delta = diff_delta(src, src_size,
- dst, dst_size,
- &delta_size, delta_limit);
- if (!delta)
- /* If delta_limit is exceeded, we have too much differences */
- return -1;
+ unsigned int accum1, accum2, i;
- /* Estimate the edit size by interpreting delta. */
- if (count_delta(delta, delta_size, src_copied, literal_added)) {
- free(delta);
- return -1;
+ /* an 8-byte shift register made of accum1 and accum2. New
+ * bytes come at LSB of accum2, and shifted up to accum1
+ */
+ for (i = accum1 = accum2 = 0; i < 7; i++, sz--) {
+ accum1 = (accum1 << 8) | (accum2 >> 24);
+ accum2 = (accum2 << 8) | *buf++;
+ }
+ while (sz) {
+ accum1 = (accum1 << 8) | (accum2 >> 24);
+ accum2 = (accum2 << 8) | *buf++;
+ /* We want something that hashes permuted byte
+ * sequences nicely; simpler hash like (accum1 ^
+ * accum2) does not perform as well.
+ */
+ i = (accum1 + accum2 * 0x61) % HASHBASE;
+ count[i]++;
+ sz--;
}
- free(delta);
- return 0;
}
int diffcore_count_changes(void *src, unsigned long src_size,
@@ -35,9 +56,28 @@ int diffcore_count_changes(void *src, unsigned long src_size,
unsigned long *src_copied,
unsigned long *literal_added)
{
- return diffcore_count_changes_1(src, src_size,
- dst, dst_size,
- delta_limit,
- src_copied,
- literal_added);
+ int *src_count, *dst_count, i;
+ unsigned long sc, la;
+
+ if (src_size < 8 || dst_size < 8)
+ return -1;
+
+ src_count = xcalloc(HASHBASE * 2, sizeof(int));
+ dst_count = src_count + HASHBASE;
+ hash_chars(src, src_size, src_count);
+ hash_chars(dst, dst_size, dst_count);
+
+ sc = la = 0;
+ for (i = 0; i < HASHBASE; i++) {
+ if (src_count[i] < dst_count[i]) {
+ la += dst_count[i] - src_count[i];
+ sc += src_count[i];
+ }
+ else /* i.e. if (dst_count[i] <= src_count[i]) */
+ sc += dst_count[i];
+ }
+ *src_copied = sc;
+ *literal_added = la;
+ free(src_count);
+ return 0;
}
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 55cf1c37f3..625b589fb7 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -170,19 +170,15 @@ static int estimate_similarity(struct diff_filespec *src,
&src_copied, &literal_added))
return 0;
- /* Extent of damage */
- if (src->size + literal_added < src_copied)
- delta_size = 0;
- else
- delta_size = (src->size - src_copied) + literal_added;
-
- /*
- * Now we will give some score to it. 100% edit gets 0 points
- * and 0% edit gets MAX_SCORE points.
+ /* How similar are they?
+ * what percentage of material in dst are from source?
*/
- score = MAX_SCORE - (MAX_SCORE * delta_size / base_size);
- if (score < 0) return 0;
- if (MAX_SCORE < score) return MAX_SCORE;
+ if (dst->size < src_copied)
+ score = MAX_SCORE;
+ else if (!dst->size)
+ score = 0; /* should not happen */
+ else
+ score = src_copied * MAX_SCORE / dst->size;
return score;
}
diff --git a/diffcore.h b/diffcore.h
index dba4f17658..d31b3b476c 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -17,8 +17,8 @@
*/
#define MAX_SCORE 60000.0
#define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */
-#define DEFAULT_BREAK_SCORE 30000 /* minimum for break to happen (50%)*/
-#define DEFAULT_MERGE_SCORE 48000 /* maximum for break-merge to happen (80%)*/
+#define DEFAULT_BREAK_SCORE 30000 /* minimum for break to happen (50%) */
+#define DEFAULT_MERGE_SCORE 36000 /* maximum for break-merge to happen 60%) */
#define MINIMUM_BREAK_SIZE 400 /* do not break a file smaller than this */
diff --git a/entry.c b/entry.c
index 8fb99bc83f..5d9aefd03f 100644
--- a/entry.c
+++ b/entry.c
@@ -63,7 +63,7 @@ static int create_file(const char *path, unsigned int mode)
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
-static int write_entry(struct cache_entry *ce, const char *path, struct checkout *state)
+static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile)
{
int fd;
void *new;
@@ -80,7 +80,11 @@ static int write_entry(struct cache_entry *ce, const char *path, struct checkout
}
switch (ntohl(ce->ce_mode) & S_IFMT) {
case S_IFREG:
- fd = create_file(path, ntohl(ce->ce_mode));
+ if (to_tempfile) {
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = mkstemp(path);
+ } else
+ fd = create_file(path, ntohl(ce->ce_mode));
if (fd < 0) {
free(new);
return error("git-checkout-index: unable to create file %s (%s)",
@@ -93,12 +97,27 @@ static int write_entry(struct cache_entry *ce, const char *path, struct checkout
return error("git-checkout-index: unable to write file %s", path);
break;
case S_IFLNK:
- if (symlink(new, path)) {
+ if (to_tempfile) {
+ strcpy(path, ".merge_link_XXXXXX");
+ fd = mkstemp(path);
+ if (fd < 0) {
+ free(new);
+ return error("git-checkout-index: unable to create "
+ "file %s (%s)", path, strerror(errno));
+ }
+ wrote = write(fd, new, size);
+ close(fd);
+ free(new);
+ if (wrote != size)
+ return error("git-checkout-index: unable to write file %s",
+ path);
+ } else {
+ wrote = symlink(new, path);
free(new);
- return error("git-checkout-index: unable to create "
- "symlink %s (%s)", path, strerror(errno));
+ if (wrote)
+ return error("git-checkout-index: unable to create "
+ "symlink %s (%s)", path, strerror(errno));
}
- free(new);
break;
default:
free(new);
@@ -113,12 +132,15 @@ static int write_entry(struct cache_entry *ce, const char *path, struct checkout
return 0;
}
-int checkout_entry(struct cache_entry *ce, struct checkout *state)
+int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
{
- struct stat st;
static char path[MAXPATHLEN+1];
+ struct stat st;
int len = state->base_dir_len;
+ if (topath)
+ return write_entry(ce, topath, state, 1);
+
memcpy(path, state->base_dir, len);
strcpy(path + len, ce->name);
@@ -144,10 +166,10 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state)
return error("%s is a directory", path);
remove_subtree(path);
}
- } else if (state->not_new)
+ } else if (state->not_new)
return 0;
create_directories(path, state);
- return write_entry(ce, path, state);
+ return write_entry(ce, path, state, 0);
}
diff --git a/read-tree.c b/read-tree.c
index be29b3fe11..1c3b09beff 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -337,7 +337,7 @@ static void check_updates(struct cache_entry **src, int nr)
if (ce->ce_flags & mask) {
ce->ce_flags &= ~mask;
if (update)
- checkout_entry(ce, &state);
+ checkout_entry(ce, &state, NULL);
}
}
if (total) {
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
new file mode 100755
index 0000000000..c100959cad
--- /dev/null
+++ b/t/t2004-checkout-cache-temp.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='git-checkout-index --temp test.
+
+With --temp flag, git-checkout-index writes to temporary merge files
+rather than the tracked path.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+mkdir asubdir &&
+echo tree1path0 >path0 &&
+echo tree1path1 >path1 &&
+echo tree1path3 >path3 &&
+echo tree1path4 >path4 &&
+echo tree1asubdir/path5 >asubdir/path5 &&
+git-update-index --add path0 path1 path3 path4 asubdir/path5 &&
+t1=$(git-write-tree) &&
+rm -f path* .merge_* out .git/index &&
+echo tree2path0 >path0 &&
+echo tree2path1 >path1 &&
+echo tree2path2 >path2 &&
+echo tree2path4 >path4 &&
+git-update-index --add path0 path1 path2 path4 &&
+t2=$(git-write-tree) &&
+rm -f path* .merge_* out .git/index &&
+echo tree2path0 >path0 &&
+echo tree3path1 >path1 &&
+echo tree3path2 >path2 &&
+echo tree3path3 >path3 &&
+git-update-index --add path0 path1 path2 path3 &&
+t3=$(git-write-tree)'
+
+test_expect_success \
+'checkout one stage 0 to temporary file' '
+rm -f path* .merge_* out .git/index &&
+git-read-tree $t1 &&
+git-checkout-index --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = tree1path1'
+
+test_expect_success \
+'checkout all stage 0 to temporary files' '
+rm -f path* .merge_* out .git/index &&
+git-read-tree $t1 &&
+git-checkout-index -a --temp >out &&
+test $(wc -l <out) = 5 &&
+for f in path0 path1 path3 path4 asubdir/path5
+do
+ test $(grep $f out | cut "-d " -f2) = $f &&
+ p=$(grep $f out | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree1$f
+done'
+
+test_expect_success \
+'prepare 3-way merge' '
+rm -f path* .merge_* out .git/index &&
+git-read-tree -m $t1 $t2 $t3'
+
+test_expect_success \
+'checkout one stage 2 to temporary file' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=2 --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = tree2path1'
+
+test_expect_success \
+'checkout all stage 2 to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index --all --stage=2 --temp >out &&
+test $(wc -l <out) = 3 &&
+for f in path1 path2 path4
+do
+ test $(grep $f out | cut "-d " -f2) = $f &&
+ p=$(grep $f out | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree2$f
+done'
+
+test_expect_success \
+'checkout all stages/one file to nothing' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=all --temp -- path0 >out &&
+test $(wc -l <out) = 0'
+
+test_expect_success \
+'checkout all stages/one file to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=all --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+cut "-d " -f1 out | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s1) = tree1path1 &&
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)'
+
+test_expect_success \
+'checkout some stages/one file to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=all --temp -- path2 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path2 &&
+cut "-d " -f1 out | (read s1 s2 s3 &&
+test $s1 = . &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s2) = tree2path2 &&
+test $(cat $s3) = tree3path2)'
+
+test_expect_success \
+'checkout all stages/all files to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index -a --stage=all --temp >out &&
+test $(wc -l <out) = 5'
+
+test_expect_success \
+'-- path0: no entry' '
+test x$(grep path0 out | cut "-d " -f2) = x'
+
+test_expect_success \
+'-- path1: all 3 stages' '
+test $(grep path1 out | cut "-d " -f2) = path1 &&
+grep path1 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s1) = tree1path1 &&
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)'
+
+test_expect_success \
+'-- path2: no stage 1, have stage 2 and 3' '
+test $(grep path2 out | cut "-d " -f2) = path2 &&
+grep path2 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test $s1 = . &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s2) = tree2path2 &&
+test $(cat $s3) = tree3path2)'
+
+test_expect_success \
+'-- path3: no stage 2, have stage 1 and 3' '
+test $(grep path3 out | cut "-d " -f2) = path3 &&
+grep path3 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test $s2 = . &&
+test -f $s3 &&
+test $(cat $s1) = tree1path3 &&
+test $(cat $s3) = tree3path3)'
+
+test_expect_success \
+'-- path4: no stage 3, have stage 1 and 3' '
+test $(grep path4 out | cut "-d " -f2) = path4 &&
+grep path4 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test $s3 = . &&
+test $(cat $s1) = tree1path4 &&
+test $(cat $s2) = tree2path4)'
+
+test_expect_success \
+'-- asubdir/path5: no stage 2 and 3 have stage 1' '
+test $(grep asubdir/path5 out | cut "-d " -f2) = asubdir/path5 &&
+grep asubdir/path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test $s2 = . &&
+test $s3 = . &&
+test $(cat $s1) = tree1asubdir/path5)'
+
+test_expect_success \
+'checkout --temp within subdir' '
+(cd asubdir &&
+ git-checkout-index -a --stage=all >out &&
+ test $(wc -l <out) = 1 &&
+ test $(grep path5 out | cut "-d " -f2) = path5 &&
+ grep path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
+ test -f ../$s1 &&
+ test $s2 = . &&
+ test $s3 = . &&
+ test $(cat ../$s1) = tree1asubdir/path5)
+)'
+
+test_expect_success \
+'checkout --temp symlink' '
+rm -f path* .merge_* out .git/index &&
+ln -s b a &&
+git-update-index --add a &&
+t4=$(git-write-tree) &&
+rm -f .git/index &&
+git-read-tree $t4 &&
+git-checkout-index --temp -a >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = a &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = b'
+
+test_done