[PATCH] qpush asserts that patches do not affect unknown files

Matt Mackall mpm at selenic.com
Thu Oct 25 19:52:21 UTC 2007


Any mq folks have an opinion on this?

On Wed, Sep 26, 2007 at 12:35:45AM -0500, Chad Skeeters wrote:
> # HG changeset patch
> # User Chad Skeeters <goobsoft at yahoo.com>
> # Date 1190784604 18000
> # Node ID 4afd6e3c818a6bd9162057a38d40022e767baf62
> # Parent  8d00788ca5785e8144e20b7996f0ee3b374f6d45
> qpush asserts that patches do not affect unknown files
> * * *
> 
> diff -r 8d00788ca578 -r 4afd6e3c818a hgext/mq.py
> --- a/hgext/mq.py	Mon Sep 24 12:42:25 2007 -0500
> +++ b/hgext/mq.py	Wed Sep 26 00:30:04 2007 -0500
> @@ -599,6 +599,23 @@ class queue:
>                  else:
>                      raise util.Abort(_("local changes found"))
>          return m, a, r, d
> +
> +    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:
> +            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...
> +
> +        for f in affectedfiles:
> +            if util.lexists(repo.wjoin(f)):
> +                if f not in repo.dirstate:
> +                      raise util.Abort(_('Patch %s can not be applied on unknown file %s') % (patchname, f))
> +
>  
>      def new(self, repo, patch, *pats, **opts):
>          msg = opts.get('msg')
> @@ -781,6 +798,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 -r 8d00788ca578 -r 4afd6e3c818a mercurial/patch.py
> --- a/mercurial/patch.py	Mon Sep 24 12:42:25 2007 -0500
> +++ b/mercurial/patch.py	Wed Sep 26 00:30:04 2007 -0500
> @@ -496,6 +496,45 @@ 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 = []
> +    orig_pattern = re.compile("^((\\*{3})|(\\-{3}))\\s") # *** or +++
> +    new_pattern = re.compile("^((\\-{3})|(\\+{3}))\\s([^\\t]*)")
> +    found_orig=False;
> +    for line in file(patchfilepath):
> +        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.append(pathstrip(matcher.group(4), strip));
> +    return files
>  
>  class hunk:
>      def __init__(self, desc, num, lr, context):
> @@ -774,24 +813,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 -r 8d00788ca578 -r 4afd6e3c818a tests/test-mq-guards.out
> --- a/tests/test-mq-guards.out	Mon Sep 24 12:42:25 2007 -0500
> +++ b/tests/test-mq-guards.out	Wed Sep 26 00:30:04 2007 -0500
> @@ -133,6 +133,7 @@ 1 G b.patch
>  1 G b.patch
>  2 U c.patch
>  3 G d.patch
> +Could not detect files that will be affected by d.patch.  Can not assure that files are added and committed.
>  applying new.patch
>  skipping b.patch - guarded by ['+2']
>  applying c.patch
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-mq-qpush-unknown
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/test-mq-qpush-unknown	Wed Sep 26 00:30:04 2007 -0500
> @@ -0,0 +1,187 @@
> +#!/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 <<EOF
> +diff --git a/dir/bar b/dir/bar
> +index af8b2b3..49a5f2f 100644
> +--- a/dir/bar
> ++++ b/dir/bar
> +@@ -1,4 +1,4 @@
> + hi
> + hi
> +-hi
> ++h3
> + hi
> +diff --git a/foo b/foo
> +index af8b2b3..8953a92 100644
> +--- a/foo
> ++++ b/foo
> +@@ -1,4 +1,4 @@
> + hi
> ++h2
> + hi
> +-hi
> +-hi
> ++h4
> +EOF
> +
> +echo % qpush unknown correct
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add
> +hg commit -m "test"
> +hg qinit
> +hg qimport ../unified.patch
> +hg qpush
> +cat foo | grep h2
> +cat dir/bar | grep h3
> +hg qpop
> +cat foo | grep hi | wc -l
> +cat dir/bar | grep hi | wc -l
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown correct 2
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add
> +hg commit -m "test"
> +hg qinit
> +hg qimport ../context.patch
> +hg qpush
> +cat foo | grep h2
> +cat dir/bar | grep h3
> +hg qpop
> +cat foo | grep hi | wc -l
> +cat dir/bar | grep hi | wc -l
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown correct 3
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add
> +hg commit -m "test"
> +hg qinit
> +hg qimport ../git.patch
> +hg qpush
> +cat foo | grep h2
> +cat dir/bar | grep h3
> +hg qpop
> +cat foo | grep hi | wc -l
> +cat dir/bar | grep hi | wc -l
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown correct 4 - qpush from sub directory
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add
> +hg commit -m "test"
> +hg qinit
> +hg qimport ../git.patch
> +cd dir
> +hg qpush
> +cd ..
> +cat foo | grep h2
> +cat dir/bar | grep h3
> +hg qpop
> +cat foo | grep hi | wc -l
> +cat dir/bar | grep hi | wc -l
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown warn
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add
> +hg commit -m "test"
> +hg qinit
> +hg qimport ../ed.patch
> +hg qpush
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown warn 2
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add
> +hg commit -m "test"
> +hg qinit
> +hg qimport ../normal.patch
> +hg qpush
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown error
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add foo # but not bar
> +hg qinit
> +hg qimport ../unified.patch
> +hg qpush
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown error 2
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +hg add dir/bar # but not foo
> +hg qinit
> +hg qimport ../context.patch
> +hg qpush
> +cd ..
> +rm -rf tmp3
> +
> +echo % qpush unknown error 3
> +cp -r tmp1 tmp3
> +cd tmp3
> +hg init
> +# add nothing
> +hg qinit
> +hg qimport ../git.patch
> +hg qpush
> +cd ..
> +rm -rf tmp3
> +
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-mq-qpush-unknown.out
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/test-mq-qpush-unknown.out	Wed Sep 26 00:30:04 2007 -0500
> @@ -0,0 +1,61 @@
> +% 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 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 on unknown file dir/bar
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-mq.out
> --- a/tests/test-mq.out	Mon Sep 24 12:42:25 2007 -0500
> +++ b/tests/test-mq.out	Wed Sep 26 00:30:04 2007 -0500
> @@ -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 on unknown file 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
> @@ -287,6 +278,7 @@ copy from new
>  copy from new
>  copy to copy
>  Now at: new
> +Could not detect files that will be affected by copy.  Can not assure that files are added and committed.
>  applying copy
>  Now at: copy
>  diff --git a/new b/copy
> @@ -371,6 +363,7 @@ diff --git a/bucephalus b/bucephalus
>  diff --git a/bucephalus b/bucephalus
>  % check binary patches can be popped and pushed
>  Now at: addalexander
> +Could not detect files that will be affected by addbucephalus.  Can not assure that files are added and committed.
>  applying addbucephalus
>  Now at: addbucephalus
>  8ba2a2f3e77b55d03051ff9c24ad65e7  bucephalus
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-patch-affected
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/test-patch-affected	Wed Sep 26 00:30:04 2007 -0500
> @@ -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 <<EOF
> +diff --git a/dir/bar b/dir/bar
> +index af8b2b3..49a5f2f 100644
> +--- a/dir/bar
> ++++ b/dir/bar
> +@@ -1,4 +1,4 @@
> + hi
> + hi
> +-hi
> ++h3
> + hi
> +diff --git a/foo b/foo
> +index af8b2b3..8953a92 100644
> +--- a/foo
> ++++ b/foo
> +@@ -1,4 +1,4 @@
> + hi
> ++h2
> + hi
> +-hi
> +-hi
> ++h4
> +EOF
> +
> +cat > patch.py <<EOF
> +from mercurial import commands, cmdutil, hg, patch, revlog, util
> +def printaffected(patchfilepath):
> +    files = patch.internalgetaffected(patchfilepath);
> +    for f in files:
> +        print f;
> +
> +print("% testing getaffected: unified");
> +printaffected("unified.patch");
> +
> +print("% testing getaffected: ed");
> +printaffected("ed.patch");
> +
> +print("% testing getaffected: normal");
> +printaffected("normal.patch");
> +
> +print("% testing getaffected: context");
> +printaffected("context.patch");
> +
> +print("% testing getaffected: git");
> +printaffected("git.patch");
> +EOF
> +
> +python patch.py
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-patch-affected-external
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/test-patch-affected-external	Wed Sep 26 00:30:04 2007 -0500
> @@ -0,0 +1,47 @@
> +#!/bin/bash
> +
> +echo "[ui]" >> $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 <<EOF
> +from mercurial import commands, cmdutil, hg, patch, revlog, util
> +from mercurial import ui as _ui
> +
> +def printaffected(patchfilepath):
> +    u = _ui.ui()
> +    files = patch.getaffected(patchfilepath, u)
> +    for f in files:
> +        print f;
> +
> +print("% testing getaffected: context");
> +printaffected("context.patch");
> +EOF
> +
> +python patch.py
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-patch-affected-external.out
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/test-patch-affected-external.out	Wed Sep 26 00:30:04 2007 -0500
> @@ -0,0 +1,3 @@
> +% testing getaffected: context
> +dir/bar
> +foo
> diff -r 8d00788ca578 -r 4afd6e3c818a tests/test-patch-affected.out
> --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
> +++ b/tests/test-patch-affected.out	Wed Sep 26 00:30:04 2007 -0500
> @@ -0,0 +1,11 @@
> +% testing getaffected: unified
> +dir/bar
> +foo
> +% testing getaffected: ed
> +% testing getaffected: normal
> +% testing getaffected: context
> +dir/bar
> +foo
> +% testing getaffected: git
> +dir/bar
> +foo
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel

-- 
Mathematics is the supreme nostalgia of our time.



More information about the Mercurial-devel mailing list