qpush asserts that patches do not affect unknown files * * * diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py -599,6 +599,31 @@ class queue: else: raise util.Abort(_("local changes found")) return m, a, r, d + + def get_unknown(self, repo, affectedfiles): + unknownfiles = set([]) + for f in affectedfiles: + if util.lexists(repo.wjoin(f)): + if f not in repo.dirstate: + unknownfiles.add(f) + return unknownfiles + + def check_unknown(self, repo, patchname, patchdir=None): + if not patchdir: + patchdir = self.path + + patchpath = os.path.join(patchdir, patchname) + + affectedfiles = patch.getaffected(patchpath, self.ui, strip=1) + if len(affectedfiles) == 0: + if os.stat(patchpath).st_size > 0: + self.ui.warn(_('Could not detect files that will be affected by %s. Can not assure that files are added and committed.\n') % patchname) + # or could force check of all files unless Force is true or something like that... + + unknownfiles = self.get_unknown(repo, affectedfiles) + if len(unknownfiles) > 0: + raise util.Abort(_('Patch %s can not be applied. Patch effects unknown files %s') % (patchname, unknownfiles)) + def new(self, repo, patch, *pats, **opts): msg = opts.get('msg') -781,6 +806,11 @@ class queue: else: end = self.series.index(patch, start) + 1 s = self.series[start:end] + + if not force: + for patchname in s: + self.check_unknown(repo, patchname); + all_files = {} try: if mergeq: diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py -496,6 +496,50 @@ class patchfile: self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start)) self.rej.append(h) return -1 + +def getaffected(patchfilepath, ui, strip=1): + lsdiff = ui.config('ui', 'lsdiff') + if lsdiff: + return externalgetaffected(lsdiff, patchfilepath, strip); + else: + return internalgetaffected(patchfilepath, strip); + + +def externalgetaffected(lsdiff, patchfilepath, strip=1): + fp = os.popen('%s %s' % (lsdiff, util.shellquote(patchfilepath))) + files = [] + + for line in fp: + affectedfile = line.strip() + files.append(pathstrip(affectedfile, strip)) + code = fp.close() + if code: + raise PatchError(_("lsdiff command failed: %s") % + util.explain_exit(code)[0]) + return files; + + +def internalgetaffected(patchfilepath, strip=1): + files = set() + difftag = re.compile("^diff( --?[^\\s]*)* a/.* (b/.*)$") # doesn't handle c-style quotes + orig_pattern = re.compile("^((\\*{3})|(\\-{3}))\\s") # *** or +++ + new_pattern = re.compile("^((\\-{3})|(\\+{3}))\\s([^\\t]*)") + found_orig=False; + for line in file(patchfilepath): + diffmatcher = difftag.match(line) + if diffmatcher != None: + files.add(pathstrip(diffmatcher.group(2), strip)) + + if not found_orig: + matcher = orig_pattern.match(line); + if matcher != None: + found_orig=True + else: + found_orig=False + matcher = new_pattern.match(line) + if matcher != None: + files.add(pathstrip(matcher.group(4), strip)) + return files class hunk: def __init__(self, desc, num, lr, context): -774,24 +818,24 @@ def parsefilename(str): return s return s[:i] +def pathstrip(path, count=1): + pathlen = len(path) + i = 0 + if count == 0: + return path.rstrip() + while count > 0: + i = path.find('/', i) + if i == -1: + raise PatchError(_("unable to strip away %d dirs from %s") % + (count, path)) + i += 1 + # consume '//' in the path + while i < pathlen - 1 and path[i] == '/': + i += 1 + count -= 1 + return path[i:].rstrip() + def selectfile(afile_orig, bfile_orig, hunk, strip, reverse): - def pathstrip(path, count=1): - pathlen = len(path) - i = 0 - if count == 0: - return path.rstrip() - while count > 0: - i = path.find('/', i) - if i == -1: - raise PatchError(_("unable to strip away %d dirs from %s") % - (count, path)) - i += 1 - # consume '//' in the path - while i < pathlen - 1 and path[i] == '/': - i += 1 - count -= 1 - return path[i:].rstrip() - nulla = afile_orig == "/dev/null" nullb = bfile_orig == "/dev/null" afile = pathstrip(afile_orig, strip) diff --git a/tests/test-mq-qpush-error b/tests/test-mq-qpush-error new file mode 100755 --- /dev/null +++ b/tests/test-mq-qpush-error -0,0 +1,21 @@ +#!/bin/bash + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH + +mkdir tmp1 +mkdir tmp2 + +echo hi >> tmp1/foo +echo h2 >> tmp2/foo + +diff -uNr tmp1 tmp2 > unified.patch + +cd tmp1 +hg init +hg qinit +hg qimport ../unified.patch +hg qpush +hg qpop +#ls: foo: No such file or directory +ls -1 foo diff --git a/tests/test-mq-qpush-error.out b/tests/test-mq-qpush-error.out new file mode 100644 --- /dev/null +++ b/tests/test-mq-qpush-error.out -0,0 +1,4 @@ +adding unified.patch to series file +abort: Patch unified.patch can not be applied. Patch effects unknown files set(['foo']) +no patches applied +foo diff --git a/tests/test-mq-qpush-unknown b/tests/test-mq-qpush-unknown new file mode 100755 --- /dev/null +++ b/tests/test-mq-qpush-unknown -0,0 +1,229 @@ +#!/bin/bash + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH + +mkdir tmp1 +mkdir tmp1/dir +mkdir tmp2 +mkdir tmp2/dir + +echo hi >> tmp1/foo +echo hi >> tmp1/foo +echo hi >> tmp1/foo +echo hi >> tmp1/foo + +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar + +echo hi >> tmp2/foo +echo h2 >> tmp2/foo +echo hi >> tmp2/foo +echo h4 >> tmp2/foo + +echo hi >> tmp2/dir/bar +echo hi >> tmp2/dir/bar +echo h3 >> tmp2/dir/bar +echo hi >> tmp2/dir/bar + +diff -uNr tmp1 tmp2 > unified.patch +diff -eNr tmp1 tmp2 > ed.patch +diff -nNr tmp1 tmp2 > normal.patch +diff -cNr tmp1 tmp2 > context.patch + +# don't want to require git just to support. Pregenerated. +cat > git.patch < writebin.py < binaryfile +hg qimport ../bgit.patch +hg qpush +cd .. +rm -rf tmp3 diff --git a/tests/test-mq-qpush-unknown.out b/tests/test-mq-qpush-unknown.out new file mode 100644 --- /dev/null +++ b/tests/test-mq-qpush-unknown.out -0,0 +1,80 @@ +% qpush unknown correct +adding dir/bar +adding foo +adding unified.patch to series file +applying unified.patch +Now at: unified.patch +h2 +h3 +Patch queue now empty +4 +4 +% qpush unknown correct 2 +adding dir/bar +adding foo +adding context.patch to series file +applying context.patch +Now at: context.patch +h2 +h3 +Patch queue now empty +4 +4 +% qpush unknown correct 3 +adding dir/bar +adding foo +adding git.patch to series file +applying git.patch +Now at: git.patch +h2 +h3 +Patch queue now empty +4 +4 +% qpush unknown correct 4 - qpush from sub directory +adding dir/bar +adding foo +adding git.patch to series file +applying git.patch +Now at: git.patch +h2 +h3 +Patch queue now empty +4 +4 +% qpush unknown warn +adding dir/bar +adding foo +adding ed.patch to series file +Could not detect files that will be affected by ed.patch. Can not assure that files are added and committed. +applying ed.patch +patch failed, unable to continue (try -v) +patch ed.patch is empty +Now at: ed.patch +% qpush unknown warn 2 +adding dir/bar +adding foo +adding normal.patch to series file +Could not detect files that will be affected by normal.patch. Can not assure that files are added and committed. +applying normal.patch +/usr/bin/patch: **** Only garbage was found in the patch input. +patch failed, unable to continue (try -v) +patch normal.patch is empty +Now at: normal.patch +% qpush unknown error +adding unified.patch to series file +abort: local changes found, refresh first +% qpush unknown error 2 +adding context.patch to series file +abort: local changes found, refresh first +% qpush unknown error 3 +adding git.patch to series file +abort: Patch git.patch can not be applied. Patch effects unknown files set(['dir/bar', 'foo']) +% create bgit.patch +% bgit.patch works +adding bgit.patch to series file +applying bgit.patch +Now at: bgit.patch +% bgit.patch unknown +adding bgit.patch to series file +abort: Patch bgit.patch can not be applied. Patch effects unknown files set(['binaryfile']) diff --git a/tests/test-mq.out b/tests/test-mq.out --- a/tests/test-mq.out +++ b/tests/test-mq.out -260,19 +260,10 @@ M a M a % qpush failure Patch queue now empty -applying foo -applying bar -file foo already exists -1 out of 1 hunk FAILED -- saving rejects to file foo.rej -patch failed, unable to continue (try -v) -patch failed, rejects left in working dir -Errors during apply, please fix and refresh bar +abort: Patch bar can not be applied. Patch effects unknown files set(['foo']) ? foo -? foo.rej % mq tags -0 qparent -1 qbase foo -2 qtip bar tip +abort: unknown revision 'qparent'! new file diff --git a/new b/new diff --git a/tests/test-patch-affected b/tests/test-patch-affected new file mode 100755 --- /dev/null +++ b/tests/test-patch-affected -0,0 +1,81 @@ +#!/bin/bash + +mkdir tmp1 +mkdir tmp1/dir +mkdir tmp2 +mkdir tmp2/dir + +echo hi >> tmp1/foo +echo hi >> tmp1/foo +echo hi >> tmp1/foo +echo hi >> tmp1/foo + +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar + +echo hi >> tmp2/foo +echo h2 >> tmp2/foo +echo hi >> tmp2/foo +echo h4 >> tmp2/foo + +echo hi >> tmp2/dir/bar +echo hi >> tmp2/dir/bar +echo h3 >> tmp2/dir/bar +echo hi >> tmp2/dir/bar + +diff -uNr tmp1 tmp2 > unified.patch +diff -eNr tmp1 tmp2 > ed.patch +diff -nNr tmp1 tmp2 > normal.patch +diff -cNr tmp1 tmp2 > context.patch + +# don't want to require git just to support. Pregenerated. +cat > git.patch < patch.py <> $HGRCPATH +echo "lsdiff=lsdiff" >> $HGRCPATH + +mkdir tmp1 +mkdir tmp1/dir +mkdir tmp2 +mkdir tmp2/dir + +echo hi >> tmp1/foo +echo hi >> tmp1/foo +echo hi >> tmp1/foo +echo hi >> tmp1/foo + +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar +echo hi >> tmp1/dir/bar + +echo hi >> tmp2/foo +echo h2 >> tmp2/foo +echo hi >> tmp2/foo +echo h4 >> tmp2/foo + +echo hi >> tmp2/dir/bar +echo hi >> tmp2/dir/bar +echo h3 >> tmp2/dir/bar +echo hi >> tmp2/dir/bar + +diff -cNr tmp1 tmp2 > context.patch + +cat > patch.py <