github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/git/githistory/rewriter_test.go (about)

     1  package githistory
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"io"
     7  	"io/ioutil"
     8  	"path/filepath"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/git-lfs/git-lfs/filepathfilter"
    15  	"github.com/git-lfs/git-lfs/git/odb"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func TestRewriterRewritesHistory(t *testing.T) {
    20  	db := DatabaseFromFixture(t, "linear-history.git")
    21  	r := NewRewriter(db)
    22  
    23  	tip, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
    24  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
    25  			contents, err := ioutil.ReadAll(b.Contents)
    26  			if err != nil {
    27  				return nil, err
    28  			}
    29  
    30  			n, err := strconv.Atoi(string(contents))
    31  			if err != nil {
    32  				return nil, err
    33  			}
    34  
    35  			rewritten := strconv.Itoa(n + 1)
    36  
    37  			return &odb.Blob{
    38  				Contents: strings.NewReader(rewritten),
    39  				Size:     int64(len(rewritten)),
    40  			}, nil
    41  		},
    42  	})
    43  
    44  	assert.Nil(t, err)
    45  
    46  	tree1 := "ad0aebd16e34cf047820994ea7538a6d4a111082"
    47  	tree2 := "6e07bd31cb70c4add2c973481ad4fa38b235ca69"
    48  	tree3 := "c5decfe1fcf39b8c489f4a0bf3b3823676339f80"
    49  
    50  	// After rewriting, the HEAD state of the repository should contain a
    51  	// tree identical to:
    52  	//
    53  	//   100644 blob bf0d87ab1b2b0ec1a11a3973d2845b42413d9767   hello.txt
    54  
    55  	AssertCommitTree(t, db, hex.EncodeToString(tip), tree1)
    56  
    57  	AssertBlobContents(t, db, tree1, "hello.txt", "4")
    58  
    59  	// After rewriting, the HEAD~1 state of the repository should contain a
    60  	// tree identical to:
    61  	//
    62  	//   100644 blob e440e5c842586965a7fb77deda2eca68612b1f53   hello.txt
    63  
    64  	AssertCommitParent(t, db, hex.EncodeToString(tip), "911994ab82ce256433c1fa739dbbbc7142156289")
    65  	AssertCommitTree(t, db, "911994ab82ce256433c1fa739dbbbc7142156289", tree2)
    66  
    67  	AssertBlobContents(t, db, tree2, "hello.txt", "3")
    68  
    69  	// After rewriting, the HEAD~2 state of the repository should contain a
    70  	// tree identical to:
    71  	//
    72  	//   100644 blob d8263ee9860594d2806b0dfd1bfd17528b0ba2a4   hello.txt
    73  
    74  	AssertCommitParent(t, db, "911994ab82ce256433c1fa739dbbbc7142156289", "38679ebeba3403103196eb6272b326f96c928ace")
    75  	AssertCommitTree(t, db, "38679ebeba3403103196eb6272b326f96c928ace", tree3)
    76  
    77  	AssertBlobContents(t, db, tree3, "hello.txt", "2")
    78  }
    79  
    80  func TestRewriterRewritesOctopusMerges(t *testing.T) {
    81  	db := DatabaseFromFixture(t, "octopus-merge.git")
    82  	r := NewRewriter(db)
    83  
    84  	tip, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
    85  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
    86  			return &odb.Blob{
    87  				Contents: io.MultiReader(b.Contents, strings.NewReader("_new")),
    88  				Size:     b.Size + int64(len("_new")),
    89  			}, nil
    90  		},
    91  	})
    92  
    93  	assert.Nil(t, err)
    94  
    95  	tree := "8a56716daa78325c3d0433cc163890969810b0da"
    96  
    97  	// After rewriting, the HEAD state of the repository should contain a
    98  	// tree identical to:
    99  	//
   100  	//   100644 blob 309f7fc2bfd9ae77b4131cf9cbcc3b548c42ca57    a.txt
   101  	//   100644 blob 70470dc26cb3eef54fe3dcba53066f7ca7c495c0    b.txt
   102  	//   100644 blob f2557f74fd5b60f959baf77091782089761e2dc3    hello.txt
   103  
   104  	AssertCommitTree(t, db, hex.EncodeToString(tip), tree)
   105  
   106  	AssertBlobContents(t, db, tree, "a.txt", "a_new")
   107  	AssertBlobContents(t, db, tree, "b.txt", "b_new")
   108  	AssertBlobContents(t, db, tree, "hello.txt", "hello_new")
   109  
   110  	// And should contain the following parents:
   111  	//
   112  	//   parent 1fe2b9577d5610e8d8fb2c3030534036fb648393
   113  	//   parent ca447959bdcd20253d69b227bcc7c2e1d3126d5c
   114  
   115  	AssertCommitParent(t, db, hex.EncodeToString(tip), "89ab88fb7e11a439299aa2aa77a5d98f6629b750")
   116  	AssertCommitParent(t, db, hex.EncodeToString(tip), "adf1e9085f9dd263c1bec399b995ccfa5d994721")
   117  
   118  	// And each of those parents should contain the root commit as their own
   119  	// parent:
   120  
   121  	AssertCommitParent(t, db, "89ab88fb7e11a439299aa2aa77a5d98f6629b750", "52daca68bcf750bb86289fd95f92f5b3bd202328")
   122  	AssertCommitParent(t, db, "adf1e9085f9dd263c1bec399b995ccfa5d994721", "52daca68bcf750bb86289fd95f92f5b3bd202328")
   123  }
   124  
   125  func TestRewriterVisitsPackedObjects(t *testing.T) {
   126  	db := DatabaseFromFixture(t, "packed-objects.git")
   127  	r := NewRewriter(db)
   128  
   129  	var contents []byte
   130  
   131  	_, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
   132  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   133  			var err error
   134  
   135  			contents, err = ioutil.ReadAll(b.Contents)
   136  			if err != nil {
   137  				return nil, err
   138  			}
   139  
   140  			return &odb.Blob{
   141  				Contents: bytes.NewReader(contents),
   142  				Size:     int64(len(contents)),
   143  			}, nil
   144  		},
   145  	})
   146  
   147  	assert.NoError(t, err)
   148  	assert.Equal(t, string(contents), "Hello, world!\n")
   149  }
   150  
   151  func TestRewriterDoesntVisitUnchangedSubtrees(t *testing.T) {
   152  	db := DatabaseFromFixture(t, "repeated-subtrees.git")
   153  	r := NewRewriter(db)
   154  
   155  	seen := make(map[string]int)
   156  
   157  	_, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
   158  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   159  			seen[path] = seen[path] + 1
   160  
   161  			return b, nil
   162  		},
   163  	})
   164  
   165  	assert.Nil(t, err)
   166  
   167  	assert.Equal(t, 2, seen["a.txt"])
   168  	assert.Equal(t, 1, seen[filepath.Join("subdir", "b.txt")])
   169  }
   170  
   171  func TestRewriterVisitsUniqueEntriesWithIdenticalContents(t *testing.T) {
   172  	db := DatabaseFromFixture(t, "identical-blobs.git")
   173  	r := NewRewriter(db)
   174  
   175  	tip, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
   176  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   177  			if path == "b.txt" {
   178  				return b, nil
   179  			}
   180  
   181  			return &odb.Blob{
   182  				Contents: strings.NewReader("changed"),
   183  				Size:     int64(len("changed")),
   184  			}, nil
   185  		},
   186  	})
   187  
   188  	assert.Nil(t, err)
   189  
   190  	tree := "bbbe0a7676523ae02234bfe874784ca2380c2d4b"
   191  
   192  	AssertCommitTree(t, db, hex.EncodeToString(tip), tree)
   193  
   194  	// After rewriting, the HEAD state of the repository should contain a
   195  	// tree identical to:
   196  	//
   197  	//   100644 blob 21fb1eca31e64cd3914025058b21992ab76edcf9    a.txt
   198  	//   100644 blob 94f3610c08588440112ed977376f26a8fba169b0    b.txt
   199  
   200  	AssertBlobContents(t, db, tree, "a.txt", "changed")
   201  	AssertBlobContents(t, db, tree, "b.txt", "original")
   202  }
   203  
   204  func TestRewriterIgnoresPathsThatDontMatchFilter(t *testing.T) {
   205  	include := []string{"*.txt"}
   206  	exclude := []string{"subdir/*.txt"}
   207  
   208  	filter := filepathfilter.New(include, exclude)
   209  
   210  	db := DatabaseFromFixture(t, "non-repeated-subtrees.git")
   211  	r := NewRewriter(db, WithFilter(filter))
   212  
   213  	seen := make(map[string]int)
   214  
   215  	_, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
   216  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   217  			seen[path] = seen[path] + 1
   218  
   219  			return b, nil
   220  		},
   221  	})
   222  
   223  	assert.Nil(t, err)
   224  	assert.Equal(t, 1, seen["a.txt"])
   225  	assert.Equal(t, 0, seen[filepath.Join("subdir", "b.txt")])
   226  }
   227  
   228  func TestRewriterAllowsAdditionalTreeEntries(t *testing.T) {
   229  	db := DatabaseFromFixture(t, "linear-history.git")
   230  	r := NewRewriter(db)
   231  
   232  	extra, err := db.WriteBlob(&odb.Blob{
   233  		Contents: strings.NewReader("extra\n"),
   234  		Size:     int64(len("extra\n")),
   235  	})
   236  	assert.Nil(t, err)
   237  
   238  	tip, err := r.Rewrite(&RewriteOptions{Include: []string{"refs/heads/master"},
   239  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   240  			return b, nil
   241  		},
   242  
   243  		TreeCallbackFn: func(path string, tr *odb.Tree) (*odb.Tree, error) {
   244  			return &odb.Tree{
   245  				Entries: append(tr.Entries, &odb.TreeEntry{
   246  					Name:     "extra.txt",
   247  					Filemode: 0100644,
   248  					Oid:      extra,
   249  				}),
   250  			}, nil
   251  		},
   252  	})
   253  
   254  	assert.Nil(t, err)
   255  
   256  	tree1 := "40c2eb627a3b8e84b82a47a973d32960f3898b6a"
   257  	tree2 := "d7a5bcb69f2cd2652a014663a948952ea603c2c0"
   258  	tree3 := "45b752554d128f85bf23d7c3ddf48c47cbc345c8"
   259  
   260  	// After rewriting, the HEAD state of the repository should contain a
   261  	// tree identical to:
   262  	//
   263  	//   100644 blob e440e5c842586965a7fb77deda2eca68612b1f53    hello.txt
   264  	//   100644 blob 0f2287157f7cb0dd40498c7a92f74b6975fa2d57    extra.txt
   265  
   266  	AssertCommitTree(t, db, hex.EncodeToString(tip), tree1)
   267  
   268  	AssertBlobContents(t, db, tree1, "hello.txt", "3")
   269  	AssertBlobContents(t, db, tree1, "extra.txt", "extra\n")
   270  
   271  	// After rewriting, the HEAD~1 state of the repository should contain a
   272  	// tree identical to:
   273  	//
   274  	//   100644 blob d8263ee9860594d2806b0dfd1bfd17528b0ba2a4    hello.txt
   275  	//   100644 blob 0f2287157f7cb0dd40498c7a92f74b6975fa2d57    extra.txt
   276  
   277  	AssertCommitParent(t, db, hex.EncodeToString(tip), "54ca0fdd5ee455d872ce4b4e379abe1c4cdc39b3")
   278  	AssertCommitTree(t, db, "54ca0fdd5ee455d872ce4b4e379abe1c4cdc39b3", tree2)
   279  
   280  	AssertBlobContents(t, db, tree2, "hello.txt", "2")
   281  	AssertBlobContents(t, db, tree2, "extra.txt", "extra\n")
   282  
   283  	// After rewriting, the HEAD~2 state of the repository should contain a
   284  	// tree identical to:
   285  	//
   286  	//   100644 blob 56a6051ca2b02b04ef92d5150c9ef600403cb1de    hello.txt
   287  	//   100644 blob 0f2287157f7cb0dd40498c7a92f74b6975fa2d57    extra.txt
   288  
   289  	AssertCommitParent(t, db, "54ca0fdd5ee455d872ce4b4e379abe1c4cdc39b3", "4c52196256c611d18ad718b9b68b3d54d0a6686d")
   290  	AssertCommitTree(t, db, "4c52196256c611d18ad718b9b68b3d54d0a6686d", tree3)
   291  
   292  	AssertBlobContents(t, db, tree3, "hello.txt", "1")
   293  	AssertBlobContents(t, db, tree3, "extra.txt", "extra\n")
   294  }
   295  
   296  func TestHistoryRewriterUseOriginalParentsForPartialMigration(t *testing.T) {
   297  	db := DatabaseFromFixture(t, "linear-history-with-tags.git")
   298  	r := NewRewriter(db)
   299  
   300  	tip, err := r.Rewrite(&RewriteOptions{
   301  		Include: []string{"refs/heads/master"},
   302  		Exclude: []string{"refs/tags/middle"},
   303  
   304  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   305  			return b, nil
   306  		},
   307  	})
   308  
   309  	// After rewriting, the rewriter should have only modified the latest
   310  	// commit (HEAD), and excluded the first two, both reachable by
   311  	// refs/tags/middle.
   312  	//
   313  	// This should modify one commit, and appropriately link the parent as
   314  	// follows:
   315  	//
   316  	//   tree 20ecedad3e74a113695fe5f00ab003694e2e1e9c
   317  	//   parent 228afe30855933151f7a88e70d9d88314fd2f191
   318  	//   author Taylor Blau <me@ttaylorr.com> 1496954214 -0600
   319  	//   committer Taylor Blau <me@ttaylorr.com> 1496954214 -0600
   320  	//
   321  	//   some.txt: c
   322  
   323  	expectedParent := "228afe30855933151f7a88e70d9d88314fd2f191"
   324  
   325  	assert.NoError(t, err)
   326  	AssertCommitParent(t, db, hex.EncodeToString(tip), expectedParent)
   327  }
   328  
   329  func TestHistoryRewriterUpdatesRefs(t *testing.T) {
   330  	db := DatabaseFromFixture(t, "linear-history.git")
   331  	r := NewRewriter(db)
   332  
   333  	AssertRef(t, db,
   334  		"refs/heads/master", HexDecode(t, "e669b63f829bfb0b91fc52a5bcea53dd7977a0ee"))
   335  
   336  	tip, err := r.Rewrite(&RewriteOptions{
   337  		Include: []string{"refs/heads/master"},
   338  
   339  		UpdateRefs: true,
   340  
   341  		BlobFn: func(path string, b *odb.Blob) (*odb.Blob, error) {
   342  			suffix := strings.NewReader("_suffix")
   343  
   344  			return &odb.Blob{
   345  				Contents: io.MultiReader(b.Contents, suffix),
   346  				Size:     b.Size + int64(suffix.Len()),
   347  			}, nil
   348  		},
   349  	})
   350  
   351  	assert.Nil(t, err)
   352  
   353  	c1 := hex.EncodeToString(tip)
   354  	c2 := "86f7ba8f02edaca4f980cdd584ea8899e18b840c"
   355  	c3 := "d73b8c1a294e2371b287d9b75dbed82328ad446e"
   356  
   357  	AssertRef(t, db, "refs/heads/master", tip)
   358  
   359  	AssertCommitParent(t, db, c1, c2)
   360  	AssertCommitParent(t, db, c2, c3)
   361  }
   362  
   363  func TestHistoryRewriterReturnsFilter(t *testing.T) {
   364  	f := filepathfilter.New([]string{"a"}, []string{"b"})
   365  	r := NewRewriter(nil, WithFilter(f))
   366  
   367  	expected := reflect.ValueOf(f).Elem().Addr().Pointer()
   368  	got := reflect.ValueOf(r.Filter()).Elem().Addr().Pointer()
   369  
   370  	assert.Equal(t, expected, got,
   371  		"git/githistory: expected Rewriter.Filter() to return same *filepathfilter.Filter instance")
   372  }