def test_diff_with_rename(self):
        output = StringProcessAdapter(fixture('diff_rename'))
        diffs = Diff._index_from_patch_format(self.rorepo, output)
        self._assert_diff_format(diffs)

        assert_equal(1, len(diffs))

        diff = diffs[0]
        assert_true(diff.renamed_file)
        assert_true(diff.renamed)
        assert_equal(diff.rename_from, u'Jérôme')
        assert_equal(diff.rename_to, u'müller')
        assert_equal(diff.raw_rename_from, b'J\xc3\xa9r\xc3\xb4me')
        assert_equal(diff.raw_rename_to, b'm\xc3\xbcller')
        assert isinstance(str(diff), str)

        output = StringProcessAdapter(fixture('diff_rename_raw'))
        diffs = Diff._index_from_raw_format(self.rorepo, output)
        self.assertEqual(len(diffs), 1)
        diff = diffs[0]
        self.assertIsNotNone(diff.renamed_file)
        self.assertIsNotNone(diff.renamed)
        self.assertEqual(diff.rename_from, 'this')
        self.assertEqual(diff.rename_to, 'that')
        self.assertEqual(diff.change_type, 'R')
        self.assertEqual(diff.score, 100)
        self.assertEqual(len(list(diffs.iter_change_type('R'))), 1)
    def test_diff_with_change_in_type(self):
        output = StringProcessAdapter(fixture('diff_change_in_type'))
        diffs = Diff._index_from_patch_format(self.rorepo, output)
        self._assert_diff_format(diffs)
        assert_equal(2, len(diffs))

        diff = diffs[0]
        self.assertIsNotNone(diff.deleted_file)
        assert_equal(diff.a_path, 'this')
        assert_equal(diff.b_path, 'this')
        assert isinstance(str(diff), str)

        diff = diffs[1]
        assert_equal(diff.a_path, None)
        assert_equal(diff.b_path, 'this')
        self.assertIsNotNone(diff.new_file)
        assert isinstance(str(diff), str)

        output = StringProcessAdapter(fixture('diff_change_in_type_raw'))
        diffs = Diff._index_from_raw_format(self.rorepo, output)
        self.assertEqual(len(diffs), 1)
        diff = diffs[0]
        self.assertEqual(diff.rename_from, None)
        self.assertEqual(diff.rename_to, None)
        self.assertEqual(diff.change_type, 'T')
        self.assertEqual(len(list(diffs.iter_change_type('T'))), 1)
Example #3
0
    def test_diff_with_rename(self):
        output = StringProcessAdapter(fixture('diff_rename'))
        diffs = Diff._index_from_patch_format(self.rorepo, output.stdout)
        self._assert_diff_format(diffs)

        assert_equal(1, len(diffs))

        diff = diffs[0]
        assert_true(diff.renamed_file)
        assert_true(diff.renamed)
        assert_equal(diff.rename_from, u'Jérôme')
        assert_equal(diff.rename_to, u'müller')
        assert_equal(diff.raw_rename_from, b'J\xc3\xa9r\xc3\xb4me')
        assert_equal(diff.raw_rename_to, b'm\xc3\xbcller')
        assert isinstance(str(diff), str)

        output = StringProcessAdapter(fixture('diff_rename_raw'))
        diffs = Diff._index_from_raw_format(self.rorepo, output.stdout)
        assert len(diffs) == 1
        diff = diffs[0]
        assert diff.renamed_file
        assert diff.renamed
        assert diff.rename_from == 'this'
        assert diff.rename_to == 'that'
        assert len(list(diffs.iter_change_type('R'))) == 1
Example #4
0
    def test_diff_patch_format(self):
        # test all of the 'old' format diffs for completness - it should at least
        # be able to deal with it
        fixtures = ("diff_2", "diff_2f", "diff_f", "diff_i", "diff_mode_only",
                    "diff_new_mode", "diff_numstat", "diff_p", "diff_rename",
                    "diff_tree_numstat_root")

        for fixture_name in fixtures:
            diff_proc = StringProcessAdapter(fixture(fixture_name))
            Diff._index_from_patch_format(self.rorepo, diff_proc.stdout)
Example #5
0
    def test_list_from_string_new_mode(self):
        output = StringProcessAdapter(fixture('diff_new_mode'))
        diffs = Diff._index_from_patch_format(self.rorepo, output.stdout)
        self._assert_diff_format(diffs)

        assert_equal(1, len(diffs))
        assert_equal(10, len(diffs[0].diff.splitlines()))
Example #6
0
    def test_diff_unsafe_paths(self):
        output = StringProcessAdapter(fixture('diff_patch_unsafe_paths'))
        res = Diff._index_from_patch_format(None, output.stdout)

        # The "Additions"
        self.assertEqual(res[0].b_path, u'path/ starting with a space')
        self.assertEqual(res[1].b_path, u'path/"with-quotes"')
        self.assertEqual(res[2].b_path, u"path/'with-single-quotes'")
        self.assertEqual(res[3].b_path, u'path/ending in a space ')
        self.assertEqual(res[4].b_path, u'path/with\ttab')
        self.assertEqual(res[5].b_path, u'path/with\nnewline')
        self.assertEqual(res[6].b_path, u'path/with spaces')
        self.assertEqual(res[7].b_path, u'path/with-question-mark?')
        self.assertEqual(res[8].b_path, u'path/¯\\_(ツ)_|¯')
        self.assertEqual(res[9].b_path, u'path/💩.txt')
        self.assertEqual(res[9].b_rawpath, b'path/\xf0\x9f\x92\xa9.txt')
        self.assertEqual(res[10].b_path, u'path/�-invalid-unicode-path.txt')
        self.assertEqual(res[10].b_rawpath, b'path/\x80-invalid-unicode-path.txt')

        # The "Moves"
        # NOTE: The path prefixes a/ and b/ here are legit!  We're actually
        # verifying that it's not "a/a/" that shows up, see the fixture data.
        self.assertEqual(res[11].a_path, u'a/with spaces')       # NOTE: path a/ here legit!
        self.assertEqual(res[11].b_path, u'b/with some spaces')  # NOTE: path b/ here legit!
        self.assertEqual(res[12].a_path, u'a/ending in a space ')
        self.assertEqual(res[12].b_path, u'b/ending with space ')
        self.assertEqual(res[13].a_path, u'a/"with-quotes"')
        self.assertEqual(res[13].b_path, u'b/"with even more quotes"')
Example #7
0
 def get_local_changes(self):
     repo = self._repo
     diff_str = repo.git.diff('HEAD', '--full-index')
     diff_str = StringIO(diff_str)
     diff_str.seek(0)
     diff = Diff._index_from_patch_format(repo, diff_str)
     root = self.path
     return [os.path.relpath(di.a_blob.abspath, root) for di in diff.iter_change_type('M')]
Example #8
0
 def test_diff_of_modified_files_not_added_to_the_index(self):
     output = StringProcessAdapter(fixture('diff_abbrev-40_full-index_M_raw_no-color'))
     diffs = Diff._index_from_raw_format(self.rorepo, output.stdout)
     
     assert len(diffs) == 1, 'one modification'
     assert len(list(diffs.iter_change_type('M'))) == 1, 'one modification'
     assert diffs[0].change_type == 'M'
     assert diffs[0].b_blob is None
Example #9
0
    def get_local_changes(self, repo=None):
        repo = self._get_repo(repo)
        diff_str = repo.git.diff('--full-index')
        patches = map(str.strip, diff_str.split('diff --git'))
        patches = ['\n'.join(p.split('\n')[2:]) for p in patches[1:]]

        diff_str = StringIO(diff_str)
        diff_str.seek(0)
        index = Diff._index_from_patch_format(repo, diff_str)

        return index, patches
Example #10
0
    def get_local_changes(self, repo=None):
        repo=self._get_repo(repo)
        diff_str=repo.git.diff( '--full-index')
        patches=map(str.strip, diff_str.split('diff --git'))
        patches=['\n'.join(p.split('\n')[2:]) for p in patches[1:]]

        diff_str=StringIO(diff_str)
        diff_str.seek(0)
        index=Diff._index_from_patch_format(repo, diff_str)

        return index, patches
Example #11
0
    def test_diff_index(self):
        output = StringProcessAdapter(fixture('diff_index_patch'))
        res = Diff._index_from_patch_format(None, output.stdout)
        assert len(res) == 6
        for dr in res:
            assert dr.diff
            assert str(dr), "Diff to string conversion should be possible"
        # end for each diff

        dr = res[3]
        assert dr.diff.endswith(b"+Binary files a/rps and b/rps differ\n")
Example #12
0
    def test_diff_index(self):
        output = StringProcessAdapter(fixture('diff_index_patch'))
        res = Diff._index_from_patch_format(None, output)
        self.assertEqual(len(res), 6)
        for dr in res:
            self.assertTrue(dr.diff.startswith(b'@@'), dr)
            self.assertIsNotNone(str(dr), "Diff to string conversion should be possible")
        # end for each diff

        dr = res[3]
        assert dr.diff.endswith(b"+Binary files a/rps and b/rps differ\n")
Example #13
0
    def test_diff_index(self):
        output = StringProcessAdapter(fixture('diff_index_patch'))
        res = Diff._index_from_patch_format(None, output.stdout)
        assert len(res) == 6
        for dr in res:
            assert dr.diff
            assert str(dr), "Diff to string conversion should be possible"
        # end for each diff

        dr = res[3]
        assert dr.diff.endswith(b"+Binary files a/rps and b/rps differ\n")
Example #14
0
 def get_local_changes(self):
     repo = self._repo
     diff_str = repo.git.diff('HEAD', '--full-index')
     diff_str = StringIO(diff_str)
     diff_str.seek(0)
     diff = Diff._index_from_patch_format(repo, diff_str)
     root = self.path
     return [
         os.path.relpath(di.a_blob.abspath, root)
         for di in diff.iter_change_type('M')
     ]
Example #15
0
    def test_diff_with_rename(self):
        output = StringProcessAdapter(fixture('diff_rename'))
        diffs = Diff._index_from_patch_format(self.rorepo, output.stdout)
        self._assert_diff_format(diffs)

        assert_equal(1, len(diffs))

        diff = diffs[0]
        assert_true(diff.renamed)
        assert_equal(diff.rename_from, u'Jérôme')
        assert_equal(diff.rename_to, u'müller')
        assert isinstance(str(diff), str)

        output = StringProcessAdapter(fixture('diff_rename_raw'))
        diffs = Diff._index_from_raw_format(self.rorepo, output.stdout)
        assert len(diffs) == 1
        diff = diffs[0]
        assert diff.renamed
        assert diff.rename_from == 'this'
        assert diff.rename_to == 'that'
        assert len(list(diffs.iter_change_type('R'))) == 1
Example #16
0
    def test_diff_with_copied_file(self):
        output = StringProcessAdapter(fixture('diff_copied_mode'))
        diffs = Diff._index_from_patch_format(self.rorepo, output)
        self._assert_diff_format(diffs)

        self.assertEqual(1, len(diffs))

        diff = diffs[0]
        self.assertTrue(diff.copied_file)
        self.assertTrue(diff.a_path, 'test1.txt')
        self.assertTrue(diff.b_path, 'test2.txt')
        assert isinstance(str(diff), str)

        output = StringProcessAdapter(fixture('diff_copied_mode_raw'))
        diffs = Diff._index_from_raw_format(self.rorepo, output)
        self.assertEqual(len(diffs), 1)
        diff = diffs[0]
        self.assertEqual(diff.change_type, 'C')
        self.assertEqual(diff.score, 100)
        self.assertEqual(diff.a_path, 'test1.txt')
        self.assertEqual(diff.b_path, 'test2.txt')
        self.assertEqual(len(list(diffs.iter_change_type('C'))), 1)
Example #17
0
 def getChanges(self):
     parents = self.commit.parents
     if len(parents) > 0:
         for p in parents:
             pc = self.commit.repo.commit(p)
             return pc.diff(self.commit, create_patch=True)
     else:
         files = self.getTree().getAllFiles()
         diffs = []
         for fl in files:
             diffs.append(
                 Diff(self.commit.repo, None,
                      fl.blob.path, None, fl.blob.hexsha, None,
                      str(fl.blob.mode), True, False, None, None, ''))
         return diffs
Example #18
0
 def test_diff_index_raw_format(self):
     output = StringProcessAdapter(fixture('diff_index_raw'))
     res = Diff._index_from_raw_format(None, output)
     self.assertIsNotNone(res[0].deleted_file)
     self.assertIsNone(res[0].b_path,)
Example #19
0
 def test_diff_with_spaces(self):
     data = StringProcessAdapter(fixture('diff_file_with_spaces'))
     diff_index = Diff._index_from_patch_format(self.rorepo, data.stdout)
     assert diff_index[0].a_path is None, repr(diff_index[0].a_path)
     assert diff_index[0].b_path == u'file with spaces', repr(
         diff_index[0].b_path)
Example #20
0
 def test_diff_index_raw_format(self):
     output = StringProcessAdapter(fixture('diff_index_raw'))
     res = Diff._index_from_raw_format(None, output.stdout)
     assert res[0].deleted_file
     assert res[0].b_path is None
Example #21
0
 def test_diff_with_spaces(self):
     data = StringProcessAdapter(fixture('diff_file_with_spaces'))
     diff_index = Diff._index_from_patch_format(self.rorepo, data)
     self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path))
     self.assertEqual(diff_index[0].b_path, 'file with spaces', repr(diff_index[0].b_path))
Example #22
0
 def test_diff_index_raw_format(self):
     output = StringProcessAdapter(fixture('diff_index_raw'))
     res = Diff._index_from_raw_format(None, output)
     self.assertIsNotNone(res[0].deleted_file)
     self.assertIsNone(res[0].b_path,)
Example #23
0
 def test_diff_with_spaces(self):
     data = StringProcessAdapter(fixture('diff_file_with_spaces'))
     diff_index = Diff._index_from_patch_format(self.rorepo, data)
     self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path))
     self.assertEqual(diff_index[0].b_path, u'file with spaces', repr(diff_index[0].b_path))
Example #24
0
 def test_diff_with_spaces(self):
     data = StringProcessAdapter(fixture('diff_file_with_spaces'))
     diff_index = Diff._index_from_patch_format(self.rorepo, data.stdout)
     assert diff_index[0].a_path is None, repr(diff_index[0].a_path)
     assert diff_index[0].b_path == u'file with spaces', repr(diff_index[0].b_path)
Example #25
0
 def test_diff_index_raw_format(self):
     output = StringProcessAdapter(fixture('diff_index_raw'))
     res = Diff._index_from_raw_format(None, output.stdout)
     assert res[0].deleted_file
     assert res[0].b_path == ''
Example #26
0
def generate():
    '''
    Generate patch for specific branch.
    '''
    repo = Repo('.')

    if repo.is_dirty:
        abort('Working tree is dirty. Working tree must be clean to perform this operation.')

    if confirm('Use master as base?'):
        for branch in repo.branches:
            if branch.name == 'master':
                master = branch
    else:
        branch, master = prompt_branch_select(repo, 'Select base branch')

    name, selected_branch = prompt_branch_select(repo)

    while exists(path(name)):
        name = prompt('Directory already exists: %s.\nWhat should I call this patch?'%path(name))

    os.mkdir(path(name))

    rawpath = join(path(name), 'raw.diff')
    local('git diff --binary %s %s > %s'%(selected_branch.commit, master.commit, rawpath))

    with open(rawpath, 'r') as raw:
        changed = raw.read()

    config = RawConfigParser()
    config.add_section('target')
    config.add_section('iteration')
    config.set('target', 'branch', master.name)
    config.set('target', 'commit', master.commit)
    config.set('iteration', 'branch', selected_branch.name)
    config.set('iteration', 'commit', selected_branch.commit)

    print("Generated %s patch directory."%name)

    diffs = Diff.list_from_string(repo, changed)

    def filter(diff):
        def match(pattern):
            if fnmatch.fnmatch(diff.b_path, pattern):
                return pattern
        matches = [match for match in map(match, patterns) if not match is None]
        if not bool(matches):
            return diff

    ignore = os.path.join(os.getcwd(), '.diffignore')
    if os.path.exists(ignore):
        patterns = open(ignore, 'r').read().split('\n')
        diffs = [diff for diff in map(filter, diffs) if not diff is None]

    binary_ext = ['.png', '.gif', '.jpg', '.jpeg', '.flv', '.swf','.zip', '.gz', '.rar', '.fla']

    # TODO: change this to use git generated binary marker
    binaries = [diff for diff in diffs if splitext(diff.b_path)[1] in binary_ext]
    text = set(diffs) - set(binaries)

    config.add_section('patch')
    config.set('patch', 'binary', int(len(binaries) > 0))
    config.set('patch', 'text', int(len(text) > 0))

    print 'Generated patch config file'

    if text:
        text_patch = join(path(name), 'text.patch')
        with open(text_patch, 'w') as pf:
            for diff in text:
                pf.write('%s\n\n'%diff.diff)
        print 'Generated %s text patch'%name
    else:
        text_patch = None

    if binaries:
        binary_list = join(path(name), 'binary.changes')
        binary_base = join(path(name), 'binaries')
        os.mkdir(binary_base)
        with open(binary_list, 'w') as pf:
            for binary in binaries:
                def mkdirs(path):
                    path = join(binary_base, path)
                    try:
                        os.makedirs(path)
                    except:
                        pass
                    return path
                def extractfile(commit, path):
                    dirs, file = os.path.split(path)
                    dirs = mkdirs('%s/%s'%(commit, dirs))
                    with settings(show('stdout'), warn_only = True):
                        if commit == 'target':
                            commit_id = master.commit
                        else:
                            commit_id = selected_branch.commit
                        get_file(commit_id, path, join(binary_base, commit, path))
                if binary.a_commit is None:
                    # new file is being created
                    extractfile('iteration', binary.b_path)
                elif binary.b_commit is None:
                    # file is being removed
                    extractfile('target', binary.a_path)
                else:
                    # file is being updated or renamed
                    extractfile('target', binary.a_path)
                    extractfile('iteration', binary.b_path)

                if binary.new_file:
                    pf.write('new %s\n'%binary.b_path)
                elif binary.deleted_file:
                    pf.write('rm %s\n'%binary.b_path)
                elif binary.renamed:
                    pf.write('mv %s %s\n'%(binary.a_path, binary.b_path))
                else:
                    pf.write('up %s\n'%binary.b_path)
                    
        print 'Generated %s'%binary_list
    else:
        binary_list = None

    with open(join(path(name), 'patch.cfg'), 'wb') as configfile:
        config.write(configfile)

    os.mkdir(join(path(name), 'db'))
    source = join(path(name), 'db', 'source.sql')
    target = join(path(name), 'db', 'target.sql')
    get_file(selected_branch.commit, join('db', 'last'), source)
    get_file(master.commit, join('db', 'last'), target)
    with open(join(path(name), 'db', 'source.sql'), 'r') as sfp:
        with open(join(path(name), 'db', 'target.sql'), 'r') as tfp:
            dbchanged = md5sum(sfp) != md5sum(tfp)
    if dbchanged and confirm('Generate database patch?'):
        dbpatch.generate(join(path(name), 'db'), source, target)
    else:
        shutil.rmtree(join(path(name), 'db'), ignore_errors=True)
        config.set('patch', 'schema', 0)
        config.set('patch', 'data', 0)

    with open(join(path(name), 'patch.cfg'), 'wb') as configfile:
        config.write(configfile)

    return name, path(name)