Re: [PATCH v3] git-p4: recover from inconsistent perforce history

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 10 May 2020 at 11:17, Andrew Oakley <andrew@xxxxxxxxxxxxx> wrote:
>
> Perforce allows you commit files and directories with the same name, so
> you could have files //depot/foo and //depot/foo/bar both checked in.  A
> p4 sync of a repository in this state fails.  Deleting one of the files
> recovers the repository.
>
> When this happens we want git-p4 to recover in the same way as perforce.

Looks good to me.

Perforce changed their server to reject this kind of thing in the
2017.1 version:

    Bugs fixed in 2017.1
    #1489051 (Job #2170) **
       Submitting a file with the same name as an existing depot
       directory path (or vice versa) will now be rejected.

(Of course people will still have damaged repos even today).

I tried your test with both the 2015.1 and the 2020.1 versions, and it
worked in both cases - shouldn't it be impossible to get into the
state that git-p4 now recovers from with a newer p4d?

Luke


>
>
> Signed-off-by: Andrew Oakley <andrew@xxxxxxxxxxxxx>
> ---
>  git-p4.py                      | 43 ++++++++++++++++++++-
>  t/t9834-git-p4-file-dir-bug.sh | 70 ++++++++++++++++++++++++++++++++++
>  2 files changed, 111 insertions(+), 2 deletions(-)
>  create mode 100755 t/t9834-git-p4-file-dir-bug.sh
>
> diff --git a/git-p4.py b/git-p4.py
> index b8b2a1679e..d551efb0dd 100755
> --- a/git-p4.py
> +++ b/git-p4.py
> @@ -3214,6 +3214,42 @@ def hasBranchPrefix(self, path):
>              print('Ignoring file outside of prefix: {0}'.format(path))
>          return hasPrefix
>
> +    def findShadowedFiles(self, files, change):
> +        # Perforce allows you commit files and directories with the same name,
> +        # so you could have files //depot/foo and //depot/foo/bar both checked
> +        # in.  A p4 sync of a repository in this state fails.  Deleting one of
> +        # the files recovers the repository.
> +        #
> +        # Git will not allow the broken state to exist and only the most recent
> +        # of the conflicting names is left in the repository.  When one of the
> +        # conflicting files is deleted we need to re-add the other one to make
> +        # sure the git repository recovers in the same way as perforce.
> +        deleted = [f for f in files if f['action'] in self.delete_actions]
> +        to_check = set()
> +        for f in deleted:
> +            path = decode_path(f['path'])
> +            to_check.add(path + '/...')
> +            while True:
> +                path = path.rsplit("/", 1)[0]
> +                if path == "/" or path in to_check:
> +                    break
> +                to_check.add(path)
> +        to_check = ['%s@%s' % (wildcard_encode(p), change) for p in to_check
> +            if self.hasBranchPrefix(p)]
> +        if to_check:
> +            stat_result = p4CmdList(["-x", "-", "fstat", "-T",
> +                "depotFile,headAction,headRev,headType"], stdin=to_check)
> +            for record in stat_result:
> +                if record['code'] != 'stat':
> +                    continue
> +                if record['headAction'] in self.delete_actions:
> +                    continue
> +                files.append({
> +                    'action': 'add',
> +                    'path': record['depotFile'],
> +                    'rev': record['headRev'],
> +                    'type': record['headType']})
> +
>      def commit(self, details, files, branch, parent = "", allow_empty=False):
>          epoch = details["time"]
>          author = details["user"]
> @@ -3222,11 +3258,14 @@ def commit(self, details, files, branch, parent = "", allow_empty=False):
>          if self.verbose:
>              print('commit into {0}'.format(branch))
>
> +        files = [f for f in files
> +            if self.hasBranchPrefix(decode_path(f['path']))]
> +        self.findShadowedFiles(files, details['change'])
> +
>          if self.clientSpecDirs:
>              self.clientSpecDirs.update_client_spec_path_cache(files)
>
> -        files = [f for (f, path) in ((f, decode_path(f['path'])) for f in files)
> -            if self.inClientSpec(path) and self.hasBranchPrefix(path)]
> +        files = [f for f in files if self.inClientSpec(decode_path(f['path']))]
>
>          if gitConfigBool('git-p4.keepEmptyCommits'):
>              allow_empty = True
> diff --git a/t/t9834-git-p4-file-dir-bug.sh b/t/t9834-git-p4-file-dir-bug.sh
> new file mode 100755
> index 0000000000..031e1f8668
> --- /dev/null
> +++ b/t/t9834-git-p4-file-dir-bug.sh
> @@ -0,0 +1,70 @@
> +#!/bin/sh
> +
> +test_description='git p4 directory/file bug handling
> +
> +This test creates files and directories with the same name in perforce and
> +checks that git-p4 recovers from the error at the same time as the perforce
> +repository.'
> +
> +. ./lib-git-p4.sh
> +
> +test_expect_success 'start p4d' '
> +       start_p4d &&
> +       test_might_fail p4 configure set submit.collision.check=0
> +'
> +
> +test_expect_success 'init depot' '
> +       (
> +               cd "$cli" &&
> +
> +               touch add_file_add_dir_del_file add_file_add_dir_del_dir &&
> +               p4 add add_file_add_dir_del_file add_file_add_dir_del_dir &&
> +               mkdir add_dir_add_file_del_file add_dir_add_file_del_dir &&
> +               touch add_dir_add_file_del_file/file add_dir_add_file_del_dir/file &&
> +               p4 add add_dir_add_file_del_file/file add_dir_add_file_del_dir/file &&
> +               p4 submit -d "add initial" &&
> +
> +               rm -f add_file_add_dir_del_file add_file_add_dir_del_dir &&
> +               mkdir add_file_add_dir_del_file add_file_add_dir_del_dir &&
> +               touch add_file_add_dir_del_file/file add_file_add_dir_del_dir/file &&
> +               p4 add add_file_add_dir_del_file/file add_file_add_dir_del_dir/file &&
> +               rm -rf add_dir_add_file_del_file add_dir_add_file_del_dir &&
> +               touch add_dir_add_file_del_file add_dir_add_file_del_dir &&
> +               p4 add add_dir_add_file_del_file add_dir_add_file_del_dir &&
> +               p4 submit -d "add conflicting" &&
> +
> +               p4 delete -k add_file_add_dir_del_file &&
> +               p4 delete -k add_file_add_dir_del_dir/file &&
> +               p4 delete -k add_dir_add_file_del_file &&
> +               p4 delete -k add_dir_add_file_del_dir/file &&
> +               p4 submit -d "delete conflicting" &&
> +
> +               p4 delete -k "add_file_add_dir_del_file/file" &&
> +               p4 delete -k "add_file_add_dir_del_dir" &&
> +               p4 delete -k "add_dir_add_file_del_file/file" &&
> +               p4 delete -k "add_dir_add_file_del_dir" &&
> +               p4 submit -d "delete remaining"
> +       )
> +'
> +
> +test_expect_success 'clone with git-p4' '
> +       git p4 clone --dest="$git" //depot/@1,3
> +'
> +
> +test_expect_success 'check contents' '
> +       test_path_is_dir "$git/add_file_add_dir_del_file" &&
> +       test_path_is_file "$git/add_file_add_dir_del_dir" &&
> +       test_path_is_dir "$git/add_dir_add_file_del_file" &&
> +       test_path_is_file "$git/add_dir_add_file_del_dir"
> +'
> +
> +test_expect_success 'rebase and check empty' '
> +       git -C "$git" p4 rebase &&
> +
> +       test_path_is_missing "$git/add_file_add_dir_del_file" &&
> +       test_path_is_missing "$git/add_file_add_dir_del_dir" &&
> +       test_path_is_missing "$git/add_dir_add_file_del_file" &&
> +       test_path_is_missing "$git/add_dir_add_file_del_dir"
> +'
> +
> +test_done
> --
> 2.24.1
>



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux