• 
      

    Allow rbt patch to work in SVN subdirectories

    Review Request #7110 — Created March 24, 2015 and submitted

    Information

    RBTools
    release-0.7.x
    7fd84ee...

    Reviewers

    Previously rbt patch would not work in a subdirectory or subdirectory
    checkout. This was because the incorrect number of leading directories
    were being stripped (due to our SVN diffs having absolute paths). We now
    compute the correct number of leading directories to strip when applying
    patches and they now apply correctly.

    If a patch for SVN is generated in a different directory, files included
    in the patch that are not under the current directory will not be
    applied. If this occurs, the user will be informed.

    This patch also fixes the issue of passing a value --px flag on the
    commandline causing a formatting error.

    Ran unit tests.

    Manually tested the cases that gmyers outlined in
    https://reviews.reviewboard.org/r/7095/. rbt patch works in all of
    these cases.

    Description From Last Updated

    I don't understand what you are trying to protect against here. If I remove this, then all of the test …

    gmyersgmyers

    Very minor, but should this be self.INDEX_FILE_RE?

    gmyersgmyers

    Not sure if this is important, but I was looking at filter_diff() in utils/diffs.py which follows a similar recipe, and …

    gmyersgmyers

    local variable 'filtered_diff' is assigned to but never used

    reviewbotreviewbot
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
      
      
    2. 
        
    brennie
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
          rbtools/clients/errors.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
          rbtools/clients/errors.py
      
      
    2. 
        
    gmyers
    1. 
        
    2. rbtools/clients/svn.py (Diff revision 2)
       
       
       
       
       
       
       
       
      Show all issues

      I don't understand what you are trying to protect against here. If I remove this, then all of the test cases from https://reviews.reviewboard.org/r/7095/ work correctly, whereas with this code in place cases 3 and 5 throw the exception.

      As far as I understand it base_dir is metadata that describes where rbt post was executed from in terms of the directory hierarchy in the repository. But, for any particular file in a diff, it doesn't matter where rbt post is executed from because the path in the diff is always an absolute path. So in 7095, cases 1 and 2 have different base_dir metadata but the contents of the diff are identical:

      Index: /trunk/a.txt
      ===================================================================
      --- /trunk/a.txt        (revision 4)
      +++ /trunk/a.txt        (working copy)
      @@ -1 +1,2 @@
       This is a file.
      +I'm from wcRoot
      

      base_path is analogous to base_dir in that it describes where the current working directory (on the local filesystem) in the working copy is in terms of the repository hierarchy. For the purposes of patching we need to know this current location in the hierarchy so we can strip the appropriate number of leading paths from the absolute paths in the diff. I don't see why base_dir, which again only describes where the post originated from, should have any bearing on where and how the patch can be applied.

      Is there a case that I'm overlooking?

      1. Say we have the following folder structure

        /
          trunk/
            foo.txt
          branches/
            some_branch/
              bar.txt
          tags/
        

        When we run rbt diff in / our patch will have things from trunk/ and branches/.

        If we then apply the patch in, say, trunk/ we will end up with the following because of how svn patch works with --strip:

        /
          trunk/
            foo.txt
            some_branch/
              bar.txt
          branches/
          tags/
        

        Which is not what we want. That is why we guard against doing a patch in a subdirectory of where the patch was generated.

      2. What you describe would indeed occur in the case where some_branch/bar.txt is a new file (and I think new directory as well) and thus maps to an add operation in the diff. If it were only a modified file then it would be rejected by svn patch as a missing target. I imagine you could also construct a case where a file is inadvertently deleted when you have matching file names in sibling subdirectories.

        I understand the thought process here, but I still think it is too restrictive. I think there has to be some onus on the user to understand the contents/scope of the diff and then attempt to apply the patch in the correct place. Here is an example of why I do not like this restriction:

        Suppose the basic trunk, branches, tags hierarchy. A colleague may, for whatever reason, have checked out the repo at root, but only made changes to trunk, and then run rbt post from the root of their working copy. base_dir in this case is /.

        I, on the other hand, may have a checkout of this repo at trunk. I would like to apply this patch, and I know that it is a reasonable thing to do because it only applies to trunk. Executing rbt patch from the "root" of my working copy, where base_path is /trunk, results in:

        "This patch was generated in the / directory. You are currently in the /trunk directory. You cannot apply it from this location. Change to the / directory to apply this patch."

        There is no way possible for me to "change to the / directory" in my current working copy. My only option, if I wish to apply this diff automatically with rbt patch, is to checkout an entirely new working copy relative to the repo root and run rbt patch from there. I can work around this with rbt patch --print to dump the diff to file and then manually apply it with svn patch --strip=N, but I think it is a stretch for the average user to know they could do this when they have just been hit with the previous exception.

      3. This latest patch will exclude files not under the directory where the patch is being applied. In other words, if a patch generated in / is applied at /trunk, then all changes not under /trunk will not be applied. The user is warned in this case.

    3. 
        
    brennie
    reviewbot
    1. Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
      
      
      
      Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
      
      
    2. rbtools/clients/svn.py (Diff revision 3)
       
       
      Show all issues
       local variable 'filtered_diff' is assigned to but never used
      
    3. 
        
    brennie
    reviewbot
    1. Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
      
      
      
      Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
      
      
    2. 
        
    brennie
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
      
      
    2. 
        
    brennie
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
      
      
    2. 
        
    david
    1. Ship It!
    2. 
        
    gmyers
    1. Looks great, Barret!  I really like the new filtering approach.
    2. rbtools/clients/svn.py (Diff revisions 2 - 6)
       
       
      Show all issues

      Very minor, but should this be self.INDEX_FILE_RE?

    3. rbtools/clients/svn.py (Diff revisions 2 - 6)
       
       
      Show all issues

      Not sure if this is important, but I was looking at filter_diff() in utils/diffs.py which follows a similar recipe, and there m.group(1).decode('utf-8') is used.

    4. rbtools/clients/svn.py (Diff revisions 2 - 6)
       
       

      Is there any need to and/or value in returning early from apply_patch() or returning a different PatchResult.applied value in the special case where all files are excluded?

      1. That is actually a good case that I had not considered. I will add it in the next revision.

    5. 
        
    brennie
    reviewbot
    1. Tool: Pyflakes
      Processed Files:
          rbtools/clients/svn.py
      
      
      
      Tool: PEP8 Style Checker
      Processed Files:
          rbtools/clients/svn.py
      
      
    2. 
        
    brennie
    Review request changed
    Status:
    Completed
    Change Summary:
    Pushed to release-0.7.x (dce02e4)