зеркало из https://github.com/microsoft/git.git
read-tree: further loosen "working file will be lost" check.
This follows up commit ed93b449
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>
This commit is contained in:
Родитель
8ebe185bbf
Коммит
f8a9d42872
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
|
4
dir.c
4
dir.c
|
@ -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];
|
||||
|
@ -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];
|
||||
|
||||
|
|
3
dir.h
3
dir.h
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче