Mercurial > hg
comparison mercurial/patch.py @ 45098:20a65e397943
patch: refactor content diffing part in separate fn so extensions can wrap
Right now extdiff uses it's own logic using archival to diff two versions of
file using external diff tools. This makes the extdiff functionality
non-extensible.
This series is an attempt to refactor core patch and diff functionality so that
extdiff can wrap and reuse it. This will help us in using external diffing tools
at more places and not just extdiff command only then.
Differential Revision: https://phab.mercurial-scm.org/D8685
author | Pulkit Goyal <7895pulkit@gmail.com> |
---|---|
date | Mon, 06 Jul 2020 15:31:53 +0530 |
parents | a56ba57c837d |
children | 10f48720ef95 |
comparison
equal
deleted
inserted
replaced
45097:dff208398ede | 45098:20a65e397943 |
---|---|
2920 ): | 2920 ): |
2921 continue | 2921 continue |
2922 yield f1, f2, copyop | 2922 yield f1, f2, copyop |
2923 | 2923 |
2924 | 2924 |
2925 def _gitindex(text): | |
2926 if not text: | |
2927 text = b"" | |
2928 l = len(text) | |
2929 s = hashutil.sha1(b'blob %d\0' % l) | |
2930 s.update(text) | |
2931 return hex(s.digest()) | |
2932 | |
2933 | |
2934 _gitmode = {b'l': b'120000', b'x': b'100755', b'': b'100644'} | |
2935 | |
2936 | |
2925 def trydiff( | 2937 def trydiff( |
2926 repo, | 2938 repo, |
2927 revs, | 2939 revs, |
2928 ctx1, | 2940 ctx1, |
2929 ctx2, | 2941 ctx2, |
2942 losedatafn is not None, it will be called. | 2954 losedatafn is not None, it will be called. |
2943 | 2955 |
2944 pathfn is applied to every path in the diff output. | 2956 pathfn is applied to every path in the diff output. |
2945 ''' | 2957 ''' |
2946 | 2958 |
2947 def gitindex(text): | |
2948 if not text: | |
2949 text = b"" | |
2950 l = len(text) | |
2951 s = hashutil.sha1(b'blob %d\0' % l) | |
2952 s.update(text) | |
2953 return hex(s.digest()) | |
2954 | |
2955 if opts.noprefix: | 2959 if opts.noprefix: |
2956 aprefix = bprefix = b'' | 2960 aprefix = bprefix = b'' |
2957 else: | 2961 else: |
2958 aprefix = b'a/' | 2962 aprefix = b'a/' |
2959 bprefix = b'b/' | 2963 bprefix = b'b/' |
2965 def isempty(fctx): | 2969 def isempty(fctx): |
2966 return fctx is None or fctx.size() == 0 | 2970 return fctx is None or fctx.size() == 0 |
2967 | 2971 |
2968 date1 = dateutil.datestr(ctx1.date()) | 2972 date1 = dateutil.datestr(ctx1.date()) |
2969 date2 = dateutil.datestr(ctx2.date()) | 2973 date2 = dateutil.datestr(ctx2.date()) |
2970 | |
2971 gitmode = {b'l': b'120000', b'x': b'100755', b'': b'100644'} | |
2972 | 2974 |
2973 if not pathfn: | 2975 if not pathfn: |
2974 pathfn = lambda f: f | 2976 pathfn = lambda f: f |
2975 | 2977 |
2976 for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts): | 2978 for f1, f2, copyop in _filepairs(modified, added, removed, copy, opts): |
3021 if opts.git: | 3023 if opts.git: |
3022 header.append( | 3024 header.append( |
3023 b'diff --git %s%s %s%s' % (aprefix, path1, bprefix, path2) | 3025 b'diff --git %s%s %s%s' % (aprefix, path1, bprefix, path2) |
3024 ) | 3026 ) |
3025 if not f1: # added | 3027 if not f1: # added |
3026 header.append(b'new file mode %s' % gitmode[flag2]) | 3028 header.append(b'new file mode %s' % _gitmode[flag2]) |
3027 elif not f2: # removed | 3029 elif not f2: # removed |
3028 header.append(b'deleted file mode %s' % gitmode[flag1]) | 3030 header.append(b'deleted file mode %s' % _gitmode[flag1]) |
3029 else: # modified/copied/renamed | 3031 else: # modified/copied/renamed |
3030 mode1, mode2 = gitmode[flag1], gitmode[flag2] | 3032 mode1, mode2 = _gitmode[flag1], _gitmode[flag2] |
3031 if mode1 != mode2: | 3033 if mode1 != mode2: |
3032 header.append(b'old mode %s' % mode1) | 3034 header.append(b'old mode %s' % mode1) |
3033 header.append(b'new mode %s' % mode2) | 3035 header.append(b'new mode %s' % mode2) |
3034 if copyop is not None: | 3036 if copyop is not None: |
3035 if opts.showsimilarity: | 3037 if opts.showsimilarity: |
3069 if fctx1 is not None: | 3071 if fctx1 is not None: |
3070 content1 = fctx1.data() | 3072 content1 = fctx1.data() |
3071 if fctx2 is not None: | 3073 if fctx2 is not None: |
3072 content2 = fctx2.data() | 3074 content2 = fctx2.data() |
3073 | 3075 |
3074 if binary and opts.git and not opts.nobinary: | 3076 data1 = (ctx1, fctx1, path1, flag1, content1, date1) |
3075 text = mdiff.b85diff(content1, content2) | 3077 data2 = (ctx2, fctx2, path2, flag2, content2, date2) |
3076 if text: | 3078 yield diffcontent(data1, data2, header, binary, opts) |
3077 header.append( | 3079 |
3078 b'index %s..%s' % (gitindex(content1), gitindex(content2)) | 3080 |
3081 def diffcontent(data1, data2, header, binary, opts): | |
3082 """ diffs two versions of a file. | |
3083 | |
3084 data1 and data2 are tuples containg: | |
3085 | |
3086 * ctx: changeset for the file | |
3087 * fctx: file context for that file | |
3088 * path1: name of the file | |
3089 * flag: flags of the file | |
3090 * content: full content of the file (can be null in case of binary) | |
3091 * date: date of the changeset | |
3092 | |
3093 header: the patch header | |
3094 binary: whether the any of the version of file is binary or not | |
3095 opts: user passed options | |
3096 | |
3097 It exists as a separate function so that extensions like extdiff can wrap | |
3098 it and use the file content directly. | |
3099 """ | |
3100 | |
3101 ctx1, fctx1, path1, flag1, content1, date1 = data1 | |
3102 ctx2, fctx2, path2, flag2, content2, date2 = data2 | |
3103 if binary and opts.git and not opts.nobinary: | |
3104 text = mdiff.b85diff(content1, content2) | |
3105 if text: | |
3106 header.append( | |
3107 b'index %s..%s' % (_gitindex(content1), _gitindex(content2)) | |
3108 ) | |
3109 hunks = ((None, [text]),) | |
3110 else: | |
3111 if opts.git and opts.index > 0: | |
3112 flag = flag1 | |
3113 if flag is None: | |
3114 flag = flag2 | |
3115 header.append( | |
3116 b'index %s..%s %s' | |
3117 % ( | |
3118 _gitindex(content1)[0 : opts.index], | |
3119 _gitindex(content2)[0 : opts.index], | |
3120 _gitmode[flag], | |
3079 ) | 3121 ) |
3080 hunks = ((None, [text]),) | |
3081 else: | |
3082 if opts.git and opts.index > 0: | |
3083 flag = flag1 | |
3084 if flag is None: | |
3085 flag = flag2 | |
3086 header.append( | |
3087 b'index %s..%s %s' | |
3088 % ( | |
3089 gitindex(content1)[0 : opts.index], | |
3090 gitindex(content2)[0 : opts.index], | |
3091 gitmode[flag], | |
3092 ) | |
3093 ) | |
3094 | |
3095 uheaders, hunks = mdiff.unidiff( | |
3096 content1, | |
3097 date1, | |
3098 content2, | |
3099 date2, | |
3100 path1, | |
3101 path2, | |
3102 binary=binary, | |
3103 opts=opts, | |
3104 ) | 3122 ) |
3105 header.extend(uheaders) | 3123 |
3106 yield fctx1, fctx2, header, hunks | 3124 uheaders, hunks = mdiff.unidiff( |
3125 content1, | |
3126 date1, | |
3127 content2, | |
3128 date2, | |
3129 path1, | |
3130 path2, | |
3131 binary=binary, | |
3132 opts=opts, | |
3133 ) | |
3134 header.extend(uheaders) | |
3135 return fctx1, fctx2, header, hunks | |
3107 | 3136 |
3108 | 3137 |
3109 def diffstatsum(stats): | 3138 def diffstatsum(stats): |
3110 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False | 3139 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False |
3111 for f, a, r, b in stats: | 3140 for f, a, r, b in stats: |