Skip to content

Commit

Permalink
read-tree: further loosen "working file will be lost" check.
Browse files Browse the repository at this point in the history
This follows up commit ed93b44 where we removed overcautious
"working file will be lost" check.

A new option "--exclude-per-directory=.gitignore" can be used to
tell the "git-read-tree" command that the user does not mind
losing contents in untracked files in the working tree, if they
need to be overwritten by a merge (either a two-way "switch
branches" merge, or a three-way merge).

Signed-off-by: Junio C Hamano <junkio@cox.net>
  • Loading branch information
Junio C Hamano committed Dec 6, 2006
1 parent 8ebe185 commit f8a9d42
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 15 deletions.
20 changes: 20 additions & 0 deletions builtin-read-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "tree-walk.h"
#include "cache-tree.h"
#include "unpack-trees.h"
#include "dir.h"
#include "builtin.h"

static struct object_list *trees;
Expand Down Expand Up @@ -178,6 +179,23 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
continue;
}

if (!strncmp(arg, "--exclude-per-directory=", 24)) {
struct dir_struct *dir;

if (opts.dir)
die("more than one --exclude-per-directory are given.");

dir = calloc(1, sizeof(*opts.dir));
dir->show_ignored = 1;
dir->exclude_per_dir = arg + 24;
opts.dir = dir;
/* We do not need to nor want to do read-directory
* here; we are merely interested in reusing the
* per directory ignore stack mechanism.
*/
continue;
}

/* using -u and -i at the same time makes no sense */
if (1 < opts.index_only + opts.update)
usage(read_tree_usage);
Expand All @@ -190,6 +208,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
}
if ((opts.update||opts.index_only) && !opts.merge)
usage(read_tree_usage);
if ((opts.dir && !opts.update))
die("--exclude-per-directory is meaningless unless -u");

if (opts.prefix) {
int pfxlen = strlen(opts.prefix);
Expand Down
4 changes: 2 additions & 2 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
die("cannot use %s as an exclude file", fname);
}

static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
{
char exclude_file[PATH_MAX];
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
Expand All @@ -170,7 +170,7 @@ static int push_exclude_per_directory(struct dir_struct *dir, const char *base,
return current_nr;
}

static void pop_exclude_per_directory(struct dir_struct *dir, int stk)
void pop_exclude_per_directory(struct dir_struct *dir, int stk)
{
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];

Expand Down
3 changes: 3 additions & 0 deletions dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ extern int common_prefix(const char **pathspec);
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);

extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
extern int push_exclude_per_directory(struct dir_struct *, const char *, int);
extern void pop_exclude_per_directory(struct dir_struct *, int);

extern int excluded(struct dir_struct *, const char *);
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
extern void add_exclude(const char *string, const char *base,
Expand Down
82 changes: 74 additions & 8 deletions t/t1004-read-tree-m-u-wf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,27 @@ test_description='read-tree -m -u checks working tree files'

test_expect_success 'two-way setup' '
mkdir subdir &&
echo >file1 file one &&
echo >file2 file two &&
git update-index --add file1 file2 &&
echo >subdir/file1 file one in subdirectory &&
echo >subdir/file2 file two in subdirectory &&
git update-index --add file1 file2 subdir/file1 subdir/file2 &&
git commit -m initial &&
git branch side &&
git tag -f branch-point &&
echo file2 is not tracked on the master anymore &&
rm -f file2 &&
git update-index --remove file2 &&
git commit -a -m "master removes file2"
rm -f file2 subdir/file2 &&
git update-index --remove file2 subdir/file2 &&
git commit -a -m "master removes file2 and subdir/file2"
'

test_expect_success 'two-way not clobbering' '
echo >file2 master creates untracked file2 &&
echo >subdir/file2 master creates untracked subdir/file2 &&
if err=`git read-tree -m -u master side 2>&1`
then
echo should have complained
Expand All @@ -34,20 +38,82 @@ test_expect_success 'two-way not clobbering' '
fi
'

echo file2 >.gitignore

test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
if err=`git read-tree -m --exclude-per-directory=.gitignore master side 2>&1`
then
echo should have complained
false
else
echo "happy to see $err"
fi
'

test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
if err=`git read-tree -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
then
echo should have complained
false
else
echo "happy to see $err"
fi
'

test_expect_success 'two-way clobbering a ignored file' '
git read-tree -m -u --exclude-per-directory=.gitignore master side
'

rm -f .gitignore

# three-tree test

test_expect_success 'three-way not complaining' '
test_expect_success 'three-way not complaining on an untracked path in both' '
rm -f file2 &&
rm -f file2 subdir/file2 &&
git checkout side &&
echo >file3 file three &&
git update-index --add file3 &&
git commit -a -m "side adds file3" &&
echo >subdir/file3 file three &&
git update-index --add file3 subdir/file3 &&
git commit -a -m "side adds file3 and removes file2" &&
git checkout master &&
echo >file2 file two is untracked on the master side &&
echo >subdir/file2 file two is untracked on the master side &&
git-read-tree -m -u branch-point master side
'

test_expect_success 'three-way not cloberring a working tree file' '
git reset --hard &&
rm -f file2 subdir/file2 file3 subdir/file3 &&
git checkout master &&
echo >file3 file three created in master, untracked &&
echo >subdir/file3 file three created in master, untracked &&
if err=`git read-tree -m -u branch-point master side 2>&1`
then
echo should have complained
false
else
echo "happy to see $err"
fi
'

echo >.gitignore file3

test_expect_success 'three-way not complaining on an untracked file' '
git reset --hard &&
rm -f file2 subdir/file2 file3 subdir/file3 &&
git checkout master &&
echo >file3 file three created in master, untracked &&
echo >subdir/file3 file three created in master, untracked &&
git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
'

test_done
24 changes: 19 additions & 5 deletions unpack-trees.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <signal.h>
#include <sys/time.h>
#include "cache.h"
#include "dir.h"
#include "tree.h"
#include "tree-walk.h"
#include "cache-tree.h"
Expand Down Expand Up @@ -77,6 +78,12 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
{
int baselen = strlen(base);
int src_size = len + 1;
int i_stk = i_stk;
int retval = 0;

if (o->dir)
i_stk = push_exclude_per_directory(o->dir, base, strlen(base));

do {
int i;
const char *first;
Expand Down Expand Up @@ -143,7 +150,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
}
/* No name means we're done */
if (!first)
return 0;
goto leave_directory;

pathlen = strlen(first);
ce_size = cache_entry_size(baselen + pathlen);
Expand Down Expand Up @@ -240,13 +247,20 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
newbase[baselen + pathlen] = '/';
newbase[baselen + pathlen + 1] = '\0';
if (unpack_trees_rec(subposns, len, newbase, o,
indpos, df_conflict_list))
return -1;
indpos, df_conflict_list)) {
retval = -1;
goto leave_directory;
}
free(newbase);
}
free(subposns);
free(src);
} while (1);

leave_directory:
if (o->dir)
pop_exclude_per_directory(o->dir, i_stk);
return retval;
}

/* Unlink the last component and attempt to remove leading
Expand Down Expand Up @@ -456,7 +470,7 @@ static void invalidate_ce_path(struct cache_entry *ce)

/*
* We do not want to remove or overwrite a working tree file that
* is not tracked.
* is not tracked, unless it is ignored.
*/
static void verify_absent(const char *path, const char *action,
struct unpack_trees_options *o)
Expand All @@ -465,7 +479,7 @@ static void verify_absent(const char *path, const char *action,

if (o->index_only || o->reset || !o->update)
return;
if (!lstat(path, &st))
if (!lstat(path, &st) && !(o->dir && excluded(o->dir, path)))
die("Untracked working tree file '%s' "
"would be %s by merge.", path, action);
}
Expand Down
1 change: 1 addition & 0 deletions unpack-trees.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct unpack_trees_options {
int verbose_update;
int aggressive;
const char *prefix;
struct dir_struct *dir;
merge_fn_t fn;

int head_idx;
Expand Down

0 comments on commit f8a9d42

Please sign in to comment.