add --overwrite option to ignore file conflicts
Allows for safer, more fine-grained control for overwriting files than --force's all-or-nothing approach. Implements FS#31549. Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com> Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
parent
86f5c74694
commit
04d211effa
12 changed files with 101 additions and 4 deletions
|
@ -270,6 +270,18 @@ Upgrade Options (apply to '-S' and '-U')[[UO]]
|
||||||
*\--needed*::
|
*\--needed*::
|
||||||
Do not reinstall the targets that are already up-to-date.
|
Do not reinstall the targets that are already up-to-date.
|
||||||
|
|
||||||
|
*\--overwrite* <glob>::
|
||||||
|
Bypass file conflict checks and overwrite conflicting files. If the
|
||||||
|
package that is about to be installed contains files that are already
|
||||||
|
installed and match 'glob', this option will cause all those files to be
|
||||||
|
overwritten. Using '\--overwrite' will not allow overwriting a directory
|
||||||
|
with a file or installing packages with conflicting files and directories.
|
||||||
|
Multiple patterns can be specified by separating them with a comma. May be
|
||||||
|
specified multiple times. Patterns can be negated, such that files
|
||||||
|
matching them will not be overwritten, by prefixing them with an
|
||||||
|
exclamation mark. Subsequent matches will override previous ones. A leading
|
||||||
|
literal exclamation mark or backslash needs to be escaped.
|
||||||
|
|
||||||
|
|
||||||
Query Options (apply to '-Q')[[QO]]
|
Query Options (apply to '-Q')[[QO]]
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
|
@ -830,6 +830,11 @@ int alpm_option_add_hookdir(alpm_handle_t *handle, const char *hookdir);
|
||||||
int alpm_option_remove_hookdir(alpm_handle_t *handle, const char *hookdir);
|
int alpm_option_remove_hookdir(alpm_handle_t *handle, const char *hookdir);
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
alpm_list_t *alpm_option_get_overwrite_files(alpm_handle_t *handle);
|
||||||
|
int alpm_option_set_overwrite_files(alpm_handle_t *handle, alpm_list_t *globs);
|
||||||
|
int alpm_option_add_overwrite_file(alpm_handle_t *handle, const char *glob);
|
||||||
|
int alpm_option_remove_overwrite_file(alpm_handle_t *handle, const char *glob);
|
||||||
|
|
||||||
/** Returns the logfile name. */
|
/** Returns the logfile name. */
|
||||||
const char *alpm_option_get_logfile(alpm_handle_t *handle);
|
const char *alpm_option_get_logfile(alpm_handle_t *handle);
|
||||||
/** Sets the logfile name. */
|
/** Sets the logfile name. */
|
||||||
|
|
|
@ -385,6 +385,12 @@ static alpm_list_t *alpm_db_find_file_owners(alpm_db_t* db, const char *path)
|
||||||
return owners;
|
return owners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _alpm_can_overwrite_file(alpm_handle_t *handle, const char *path)
|
||||||
|
{
|
||||||
|
return handle->trans->flags & ALPM_TRANS_FLAG_FORCE
|
||||||
|
|| _alpm_fnmatch_patterns(handle->overwrite_files, path) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Find file conflicts that may occur during the transaction.
|
* @brief Find file conflicts that may occur during the transaction.
|
||||||
*
|
*
|
||||||
|
@ -448,8 +454,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
|
||||||
/* can skip file-file conflicts when forced *
|
/* can skip file-file conflicts when forced *
|
||||||
* checking presence in p2_files detects dir-file or file-dir
|
* checking presence in p2_files detects dir-file or file-dir
|
||||||
* conflicts as the path from p1 is returned */
|
* conflicts as the path from p1 is returned */
|
||||||
if((handle->trans->flags & ALPM_TRANS_FLAG_FORCE) &&
|
if(_alpm_can_overwrite_file(handle, filename)
|
||||||
alpm_filelist_contains(p2_files, filename)) {
|
&& alpm_filelist_contains(p2_files, filename)) {
|
||||||
_alpm_log(handle, ALPM_LOG_DEBUG,
|
_alpm_log(handle, ALPM_LOG_DEBUG,
|
||||||
"%s exists in both '%s' and '%s'\n", filename,
|
"%s exists in both '%s' and '%s'\n", filename,
|
||||||
p1->name, p2->name);
|
p1->name, p2->name);
|
||||||
|
@ -654,8 +660,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(alpm_handle_t *handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* skip file-file conflicts when being forced */
|
/* skip file-file conflicts when being forced */
|
||||||
if((handle->trans->flags & ALPM_TRANS_FLAG_FORCE) &&
|
if(!S_ISDIR(lsbuf.st_mode)
|
||||||
!S_ISDIR(lsbuf.st_mode)) {
|
&& _alpm_can_overwrite_file(handle, filestr)) {
|
||||||
_alpm_log(handle, ALPM_LOG_DEBUG,
|
_alpm_log(handle, ALPM_LOG_DEBUG,
|
||||||
"conflict with file on filesystem being forced\n");
|
"conflict with file on filesystem being forced\n");
|
||||||
resolved_conflict = 1;
|
resolved_conflict = 1;
|
||||||
|
|
|
@ -283,6 +283,12 @@ alpm_list_t SYMEXPORT *alpm_option_get_ignoregroups(alpm_handle_t *handle)
|
||||||
return handle->ignoregroup;
|
return handle->ignoregroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alpm_list_t SYMEXPORT *alpm_option_get_overwrite_files(alpm_handle_t *handle)
|
||||||
|
{
|
||||||
|
CHECK_HANDLE(handle, return NULL);
|
||||||
|
return handle->overwrite_files;
|
||||||
|
}
|
||||||
|
|
||||||
alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle)
|
alpm_list_t SYMEXPORT *alpm_option_get_assumeinstalled(alpm_handle_t *handle)
|
||||||
{
|
{
|
||||||
CHECK_HANDLE(handle, return NULL);
|
CHECK_HANDLE(handle, return NULL);
|
||||||
|
@ -657,6 +663,21 @@ int SYMEXPORT alpm_option_remove_ignoregroup(alpm_handle_t *handle, const char *
|
||||||
return _alpm_option_strlist_rem(handle, &(handle->ignoregroup), grp);
|
return _alpm_option_strlist_rem(handle, &(handle->ignoregroup), grp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SYMEXPORT alpm_option_add_overwrite_file(alpm_handle_t *handle, const char *glob)
|
||||||
|
{
|
||||||
|
return _alpm_option_strlist_add(handle, &(handle->overwrite_files), glob);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SYMEXPORT alpm_option_set_overwrite_files(alpm_handle_t *handle, alpm_list_t *globs)
|
||||||
|
{
|
||||||
|
return _alpm_option_strlist_set(handle, &(handle->overwrite_files), globs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SYMEXPORT alpm_option_remove_overwrite_file(alpm_handle_t *handle, const char *glob)
|
||||||
|
{
|
||||||
|
return _alpm_option_strlist_rem(handle, &(handle->overwrite_files), glob);
|
||||||
|
}
|
||||||
|
|
||||||
int SYMEXPORT alpm_option_add_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep)
|
int SYMEXPORT alpm_option_add_assumeinstalled(alpm_handle_t *handle, const alpm_depend_t *dep)
|
||||||
{
|
{
|
||||||
alpm_depend_t *depcpy;
|
alpm_depend_t *depcpy;
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct __alpm_handle_t {
|
||||||
char *gpgdir; /* Directory where GnuPG files are stored */
|
char *gpgdir; /* Directory where GnuPG files are stored */
|
||||||
alpm_list_t *cachedirs; /* Paths to pacman cache directories */
|
alpm_list_t *cachedirs; /* Paths to pacman cache directories */
|
||||||
alpm_list_t *hookdirs; /* Paths to hook directories */
|
alpm_list_t *hookdirs; /* Paths to hook directories */
|
||||||
|
alpm_list_t *overwrite_files; /* Paths that may be overwritten */
|
||||||
|
|
||||||
/* package lists */
|
/* package lists */
|
||||||
alpm_list_t *noupgrade; /* List of packages NOT to be upgraded */
|
alpm_list_t *noupgrade; /* List of packages NOT to be upgraded */
|
||||||
|
|
|
@ -783,6 +783,8 @@ static int setup_libalpm(void)
|
||||||
alpm_option_set_cachedirs(handle, config->cachedirs);
|
alpm_option_set_cachedirs(handle, config->cachedirs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alpm_option_set_overwrite_files(handle, config->overwrite_files);
|
||||||
|
|
||||||
alpm_option_set_default_siglevel(handle, config->siglevel);
|
alpm_option_set_default_siglevel(handle, config->siglevel);
|
||||||
|
|
||||||
config->localfilesiglevel = merge_siglevel(config->siglevel,
|
config->localfilesiglevel = merge_siglevel(config->siglevel,
|
||||||
|
|
|
@ -123,6 +123,7 @@ typedef struct __config_t {
|
||||||
alpm_list_t *assumeinstalled;
|
alpm_list_t *assumeinstalled;
|
||||||
alpm_list_t *noupgrade;
|
alpm_list_t *noupgrade;
|
||||||
alpm_list_t *noextract;
|
alpm_list_t *noextract;
|
||||||
|
alpm_list_t *overwrite_files;
|
||||||
char *xfercommand;
|
char *xfercommand;
|
||||||
|
|
||||||
/* our connection to libalpm */
|
/* our connection to libalpm */
|
||||||
|
@ -172,6 +173,7 @@ enum {
|
||||||
OP_GPGDIR,
|
OP_GPGDIR,
|
||||||
OP_DBONLY,
|
OP_DBONLY,
|
||||||
OP_FORCE,
|
OP_FORCE,
|
||||||
|
OP_OVERWRITE_FILES,
|
||||||
OP_COLOR,
|
OP_COLOR,
|
||||||
OP_DBPATH,
|
OP_DBPATH,
|
||||||
OP_CASCADE,
|
OP_CASCADE,
|
||||||
|
|
|
@ -190,6 +190,8 @@ static void usage(int op, const char * const myname)
|
||||||
case PM_OP_SYNC:
|
case PM_OP_SYNC:
|
||||||
case PM_OP_UPGRADE:
|
case PM_OP_UPGRADE:
|
||||||
addlist(_(" --force force install, overwrite conflicting files\n"));
|
addlist(_(" --force force install, overwrite conflicting files\n"));
|
||||||
|
addlist(_(" --overwrite <path>\n"
|
||||||
|
" overwrite conflicting files (can be used more than once)\n"));
|
||||||
addlist(_(" --asdeps install packages as non-explicitly installed\n"));
|
addlist(_(" --asdeps install packages as non-explicitly installed\n"));
|
||||||
addlist(_(" --asexplicit install packages as explicitly installed\n"));
|
addlist(_(" --asexplicit install packages as explicitly installed\n"));
|
||||||
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
|
addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
|
||||||
|
@ -708,6 +710,9 @@ static int parsearg_upgrade(int opt)
|
||||||
case OP_FORCE:
|
case OP_FORCE:
|
||||||
config->flags |= ALPM_TRANS_FLAG_FORCE;
|
config->flags |= ALPM_TRANS_FLAG_FORCE;
|
||||||
break;
|
break;
|
||||||
|
case OP_OVERWRITE_FILES:
|
||||||
|
parsearg_util_addlist(&(config->overwrite_files));
|
||||||
|
break;
|
||||||
case OP_ASDEPS:
|
case OP_ASDEPS:
|
||||||
config->flags |= ALPM_TRANS_FLAG_ALLDEPS;
|
config->flags |= ALPM_TRANS_FLAG_ALLDEPS;
|
||||||
break;
|
break;
|
||||||
|
@ -929,6 +934,7 @@ static int parseargs(int argc, char *argv[])
|
||||||
{"assume-installed", required_argument, 0, OP_ASSUMEINSTALLED},
|
{"assume-installed", required_argument, 0, OP_ASSUMEINSTALLED},
|
||||||
{"debug", optional_argument, 0, OP_DEBUG},
|
{"debug", optional_argument, 0, OP_DEBUG},
|
||||||
{"force", no_argument, 0, OP_FORCE},
|
{"force", no_argument, 0, OP_FORCE},
|
||||||
|
{"overwrite", required_argument, 0, OP_OVERWRITE_FILES},
|
||||||
{"noprogressbar", no_argument, 0, OP_NOPROGRESSBAR},
|
{"noprogressbar", no_argument, 0, OP_NOPROGRESSBAR},
|
||||||
{"noscriptlet", no_argument, 0, OP_NOSCRIPTLET},
|
{"noscriptlet", no_argument, 0, OP_NOSCRIPTLET},
|
||||||
{"ask", required_argument, 0, OP_ASK},
|
{"ask", required_argument, 0, OP_ASK},
|
||||||
|
|
|
@ -80,6 +80,9 @@ TESTS += test/pacman/tests/mode001.py
|
||||||
TESTS += test/pacman/tests/mode002.py
|
TESTS += test/pacman/tests/mode002.py
|
||||||
TESTS += test/pacman/tests/mode003.py
|
TESTS += test/pacman/tests/mode003.py
|
||||||
TESTS += test/pacman/tests/noupgrade-inverted.py
|
TESTS += test/pacman/tests/noupgrade-inverted.py
|
||||||
|
TESTS += test/pacman/tests/overwrite-files-match-negated.py
|
||||||
|
TESTS += test/pacman/tests/overwrite-files-match.py
|
||||||
|
TESTS += test/pacman/tests/overwrite-files-nonmatch.py
|
||||||
TESTS += test/pacman/tests/pacman001.py
|
TESTS += test/pacman/tests/pacman001.py
|
||||||
TESTS += test/pacman/tests/pacman002.py
|
TESTS += test/pacman/tests/pacman002.py
|
||||||
TESTS += test/pacman/tests/pacman003.py
|
TESTS += test/pacman/tests/pacman003.py
|
||||||
|
|
13
test/pacman/tests/overwrite-files-match-negated.py
Normal file
13
test/pacman/tests/overwrite-files-match-negated.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
self.description = "Install a package with an existing file matching a negated --overwrite pattern"
|
||||||
|
|
||||||
|
p = pmpkg("dummy")
|
||||||
|
p.files = ["foobar"]
|
||||||
|
self.addpkg(p)
|
||||||
|
|
||||||
|
self.filesystem = ["foobar*"]
|
||||||
|
|
||||||
|
self.args = "-U --overwrite=foobar --overwrite=!foo* %s" % p.filename()
|
||||||
|
|
||||||
|
self.addrule("!PACMAN_RETCODE=0")
|
||||||
|
self.addrule("!PKG_EXIST=dummy")
|
||||||
|
self.addrule("!FILE_MODIFIED=foobar")
|
13
test/pacman/tests/overwrite-files-match.py
Normal file
13
test/pacman/tests/overwrite-files-match.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
self.description = "Install a package with an existing file matching an --overwrite pattern"
|
||||||
|
|
||||||
|
p = pmpkg("dummy")
|
||||||
|
p.files = ["foobar"]
|
||||||
|
self.addpkg(p)
|
||||||
|
|
||||||
|
self.filesystem = ["foobar*"]
|
||||||
|
|
||||||
|
self.args = "-U --overwrite=foobar %s" % p.filename()
|
||||||
|
|
||||||
|
self.addrule("PACMAN_RETCODE=0")
|
||||||
|
self.addrule("PKG_EXIST=dummy")
|
||||||
|
self.addrule("FILE_MODIFIED=foobar")
|
13
test/pacman/tests/overwrite-files-nonmatch.py
Normal file
13
test/pacman/tests/overwrite-files-nonmatch.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
self.description = "Install a package with an existing file not matching --overwrite patterns"
|
||||||
|
|
||||||
|
p = pmpkg("dummy")
|
||||||
|
p.files = ["foobar"]
|
||||||
|
self.addpkg(p)
|
||||||
|
|
||||||
|
self.filesystem = ["foobar*"]
|
||||||
|
|
||||||
|
self.args = "-U --overwrite=foo %s" % p.filename()
|
||||||
|
|
||||||
|
self.addrule("!PACMAN_RETCODE=0")
|
||||||
|
self.addrule("!PKG_EXIST=dummy")
|
||||||
|
self.addrule("!FILE_MODIFIED=foobar")
|
Loading…
Add table
Reference in a new issue