@@ -3,6 +3,7 @@ package git
3
3
/*
4
4
#include <git2.h>
5
5
6
+ extern void _go_git_populate_apply_cb(git_apply_options *options);
6
7
extern int _go_git_diff_foreach(git_diff *diff, int eachFile, int eachHunk, int eachLine, void *payload);
7
8
extern void _go_git_setup_diff_notify_callbacks(git_diff_options* opts);
8
9
extern int _go_git_diff_blobs(git_blob *old, const char *old_path, git_blob *new, const char *new_path, git_diff_options *opts, int eachFile, int eachHunk, int eachLine, void *payload);
@@ -550,7 +551,7 @@ const (
550
551
DiffFindRemoveUnmodified DiffFindOptionsFlag = C .GIT_DIFF_FIND_REMOVE_UNMODIFIED
551
552
)
552
553
553
- //TODO implement git_diff_similarity_metric
554
+ // TODO implement git_diff_similarity_metric
554
555
type DiffFindOptions struct {
555
556
Flags DiffFindOptionsFlag
556
557
RenameThreshold uint16
@@ -847,3 +848,174 @@ func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string,
847
848
848
849
return nil
849
850
}
851
+
852
+ // ApplyHunkCallback is a callback that will be made per delta (file) when applying a patch.
853
+ type ApplyHunkCallback func (* DiffHunk ) (apply bool , err error )
854
+
855
+ // ApplyDeltaCallback is a callback that will be made per hunk when applying a patch.
856
+ type ApplyDeltaCallback func (* DiffDelta ) (apply bool , err error )
857
+
858
+ // ApplyOptions has 2 callbacks that are called for hunks or deltas
859
+ // If these functions return an error, abort the apply process immediately.
860
+ // If the first return value is true, the delta/hunk will be applied. If it is false, the delta/hunk will not be applied. In either case, the rest of the apply process will continue.
861
+ type ApplyOptions struct {
862
+ ApplyHunkCallback ApplyHunkCallback
863
+ ApplyDeltaCallback ApplyDeltaCallback
864
+ Flags uint
865
+ }
866
+
867
+ //export hunkApplyCallback
868
+ func hunkApplyCallback (_hunk * C.git_diff_hunk , _payload unsafe.Pointer ) C.int {
869
+ opts , ok := pointerHandles .Get (_payload ).(* ApplyOptions )
870
+ if ! ok {
871
+ panic ("invalid apply options payload" )
872
+ }
873
+
874
+ if opts .ApplyHunkCallback == nil {
875
+ return 0
876
+ }
877
+
878
+ hunk := diffHunkFromC (_hunk )
879
+
880
+ apply , err := opts .ApplyHunkCallback (& hunk )
881
+ if err != nil {
882
+ if gitError , ok := err .(* GitError ); ok {
883
+ return C .int (gitError .Code )
884
+ }
885
+ return - 1
886
+ } else if apply {
887
+ return 0
888
+ } else {
889
+ return 1
890
+ }
891
+ }
892
+
893
+ //export deltaApplyCallback
894
+ func deltaApplyCallback (_delta * C.git_diff_delta , _payload unsafe.Pointer ) C.int {
895
+ opts , ok := pointerHandles .Get (_payload ).(* ApplyOptions )
896
+ if ! ok {
897
+ panic ("invalid apply options payload" )
898
+ }
899
+
900
+ if opts .ApplyDeltaCallback == nil {
901
+ return 0
902
+ }
903
+
904
+ delta := diffDeltaFromC (_delta )
905
+
906
+ apply , err := opts .ApplyDeltaCallback (& delta )
907
+ if err != nil {
908
+ if gitError , ok := err .(* GitError ); ok {
909
+ return C .int (gitError .Code )
910
+ }
911
+ return - 1
912
+ } else if apply {
913
+ return 0
914
+ } else {
915
+ return 1
916
+ }
917
+ }
918
+
919
+ // DefaultApplyOptions returns default options for applying diffs or patches.
920
+ func DefaultApplyOptions () (* ApplyOptions , error ) {
921
+ opts := C.git_apply_options {}
922
+
923
+ runtime .LockOSThread ()
924
+ defer runtime .UnlockOSThread ()
925
+
926
+ ecode := C .git_apply_options_init (& opts , C .GIT_APPLY_OPTIONS_VERSION )
927
+ if int (ecode ) != 0 {
928
+
929
+ return nil , MakeGitError (ecode )
930
+ }
931
+
932
+ return applyOptionsFromC (& opts ), nil
933
+ }
934
+
935
+ func (a * ApplyOptions ) toC () * C.git_apply_options {
936
+ if a == nil {
937
+ return nil
938
+ }
939
+
940
+ opts := & C.git_apply_options {
941
+ version : C .GIT_APPLY_OPTIONS_VERSION ,
942
+ flags : C .uint (a .Flags ),
943
+ }
944
+
945
+ if a .ApplyDeltaCallback != nil || a .ApplyHunkCallback != nil {
946
+ C ._go_git_populate_apply_cb (opts )
947
+ opts .payload = pointerHandles .Track (a )
948
+ }
949
+
950
+ return opts
951
+ }
952
+
953
+ func applyOptionsFromC (opts * C.git_apply_options ) * ApplyOptions {
954
+ return & ApplyOptions {
955
+ Flags : uint (opts .flags ),
956
+ }
957
+ }
958
+
959
+ // ApplyLocation represents the possible application locations for applying
960
+ // diffs.
961
+ type ApplyLocation int
962
+
963
+ const (
964
+ // ApplyLocationWorkdir applies the patch to the workdir, leaving the
965
+ // index untouched. This is the equivalent of `git apply` with no location
966
+ // argument.
967
+ ApplyLocationWorkdir ApplyLocation = C .GIT_APPLY_LOCATION_WORKDIR
968
+ // ApplyLocationIndex applies the patch to the index, leaving the working
969
+ // directory untouched. This is the equivalent of `git apply --cached`.
970
+ ApplyLocationIndex ApplyLocation = C .GIT_APPLY_LOCATION_INDEX
971
+ // ApplyLocationBoth applies the patch to both the working directory and
972
+ // the index. This is the equivalent of `git apply --index`.
973
+ ApplyLocationBoth ApplyLocation = C .GIT_APPLY_LOCATION_BOTH
974
+ )
975
+
976
+ // ApplyDiff appllies a Diff to the given repository, making changes directly
977
+ // in the working directory, the index, or both.
978
+ func (v * Repository ) ApplyDiff (diff * Diff , location ApplyLocation , opts * ApplyOptions ) error {
979
+ runtime .LockOSThread ()
980
+ defer runtime .UnlockOSThread ()
981
+
982
+ cOpts := opts .toC ()
983
+ ecode := C .git_apply (v .ptr , diff .ptr , C .git_apply_location_t (location ), cOpts )
984
+ runtime .KeepAlive (v )
985
+ runtime .KeepAlive (diff )
986
+ runtime .KeepAlive (cOpts )
987
+ if ecode < 0 {
988
+ return MakeGitError (ecode )
989
+ }
990
+
991
+ return nil
992
+ }
993
+
994
+ // DiffFromBuffer reads the contents of a git patch file into a Diff object.
995
+ //
996
+ // The diff object produced is similar to the one that would be produced if you
997
+ // actually produced it computationally by comparing two trees, however there
998
+ // may be subtle differences. For example, a patch file likely contains
999
+ // abbreviated object IDs, so the object IDs in a git_diff_delta produced by
1000
+ // this function will also be abbreviated.
1001
+ //
1002
+ // This function will only read patch files created by a git implementation, it
1003
+ // will not read unified diffs produced by the diff program, nor any other
1004
+ // types of patch files.
1005
+ func DiffFromBuffer (buffer []byte , repo * Repository ) (* Diff , error ) {
1006
+ var diff * C.git_diff
1007
+
1008
+ cBuffer := C .CBytes (buffer )
1009
+ defer C .free (unsafe .Pointer (cBuffer ))
1010
+
1011
+ runtime .LockOSThread ()
1012
+ defer runtime .UnlockOSThread ()
1013
+
1014
+ ecode := C .git_diff_from_buffer (& diff , (* C .char )(cBuffer ), C .size_t (len (buffer )))
1015
+ if ecode < 0 {
1016
+ return nil , MakeGitError (ecode )
1017
+ }
1018
+ runtime .KeepAlive (diff )
1019
+
1020
+ return newDiffFromC (diff , repo ), nil
1021
+ }
0 commit comments