├── .travis.yml ├── README.md ├── LICENSE └── docker-rebase.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.2.1 4 | - tip 5 | notifications: 6 | # See http://about.travis-ci.org/docs/user/build-configuration/ to learn more 7 | # about configuring notification recipients and more. 8 | email: 9 | recipients: 10 | - coda.hale@gmail.com 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | docker-rebase 2 | ============= 3 | 4 | [![Build Status](https://travis-ci.org/codahale/docker-rebase.png?branch=master)](https://travis-ci.org/codahale/docker-rebase) 5 | 6 | `docker-rebase` is used to "rebase" a Docker image against an base image. The 7 | resulting image contains only those layers which are unique to the downstream 8 | image: 9 | 10 | ``` 11 | docker save base > base.tar 12 | docker save app | docker-rebase base.tar > app.tar 13 | ``` 14 | 15 | To install, run `go get -u github.com/codahale/docker-rebase`. 16 | 17 | For documentation, check [godoc](http://godoc.org/github.com/codahale/docker-rebase). 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Coda Hale 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker-rebase.go: -------------------------------------------------------------------------------- 1 | // Command docker-rebase is used to "rebase" a Docker image against an base 2 | // image. The resulting image contains only those layers which are unique to the 3 | // downstream image. 4 | // 5 | // Example 6 | // 7 | // docker save base > base.tar 8 | // docker save app | docker-rebase base.tar > app.tar 9 | package main 10 | 11 | import ( 12 | "archive/tar" 13 | "io" 14 | "log" 15 | "os" 16 | ) 17 | 18 | func main() { 19 | if len(os.Args) < 2 { 20 | log.Fatal("no base image specified") 21 | } 22 | 23 | excluded, err := ls(os.Args[1]) 24 | if err != nil { 25 | log.Panic(err) 26 | } 27 | 28 | if err := rebase(os.Stdin, os.Stdout, excluded); err != nil { 29 | log.Panic(err) 30 | } 31 | } 32 | 33 | func rebase(in io.Reader, out io.Writer, excluded map[string]bool) error { 34 | r := tar.NewReader(in) 35 | 36 | w := tar.NewWriter(out) 37 | defer w.Close() 38 | 39 | for { 40 | h, err := r.Next() 41 | if err == io.EOF { 42 | break 43 | } else if err != nil { 44 | return err 45 | } 46 | 47 | if !excluded[h.Name] { 48 | if err := w.WriteHeader(h); err != nil { 49 | return err 50 | } 51 | 52 | if _, err := io.Copy(w, r); err != nil { 53 | return err 54 | } 55 | } 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func ls(path string) (map[string]bool, error) { 62 | f, err := os.Open(path) 63 | if err != nil { 64 | return nil, err 65 | } 66 | defer f.Close() 67 | 68 | files := make(map[string]bool) 69 | r := tar.NewReader(f) 70 | for { 71 | h, err := r.Next() 72 | if err == io.EOF { 73 | break 74 | } else if err != nil { 75 | return nil, err 76 | } 77 | 78 | files[h.Name] = true 79 | } 80 | 81 | // don't exclude the metadata 82 | delete(files, "./") 83 | delete(files, "repositories") 84 | 85 | return files, nil 86 | } 87 | --------------------------------------------------------------------------------