vcs

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 7, 2018 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ChangeTypeNone means that a node did not change (compared to HEAD)
	ChangeTypeNone = ChangeType(0)
	// ChangeTypeAdd says that the node was initially added after HEAD.
	ChangeTypeAdd = ChangeType(1 << iota)
	// ChangeTypeModify says that the the node was modified after HEAD
	ChangeTypeModify
	// ChangeTypeMove says that the node was moved after HEAD.
	// Note that Move and Modify may happen at the same time.
	ChangeTypeMove
	// ChangeTypeRemove says that the node was removed after HEAD.
	ChangeTypeRemove
)
View Source
const (
	// ConflictStragetyMarker creates marker files for each conflict.
	ConflictStragetyMarker = iota

	// ConflictStragetyIgnore ignores conflicts totally.
	ConflictStragetyIgnore

	// ConflictStragetyUnknown should be used when the strategy is not clear.
	ConflictStragetyUnknown
)

Variables

This section is empty.

Functions

func ApplyPatch

func ApplyPatch(lkr *c.Linker, p *Patch) error

ApplyPatch applies the patch `p` to the linker `lkr`.

func ResetNode

func ResetNode(lkr *c.Linker, cmt *n.Commit, currPath string) (n.Node, error)

ResetNode resets a certain file to the state it had in cmt. If the file did not exist back then, it will be deleted. `nd` is usually retrieved by calling ResolveNode() and sorts.

A special case occurs when the file was moved we reset to. In this case the state of the old node (at the old path) is being written to the node at the new path. This is the more obvious choice to the user when he types:

$ brig reset HEAD^ i-was-somewhere-else-before   # name does not change.

This method returns the old node (or nil if none) and any possible error.

func Sync

func Sync(lkrSrc, lkrDst *c.Linker, cfg *SyncOptions) error

Sync will synchronize the changes from `lkrSrc` to `lkrDst`, according to the options set in `cfg`. This is atomic.

Types

type Change

type Change struct {
	// Mask is a bitmask of changes that were made.
	// It describes the change that was made between `Next` to `Head`
	// and which is part of `Head`.
	Mask ChangeType

	// Head is the commit that was the current HEAD when this change happened.
	// Note that this is NOT the commit that contains the change, but the commit before.
	Head *n.Commit

	// Next is the commit that comes before `Head`.
	Next *n.Commit

	// Curr is the node with the attributes at a specific state
	Curr n.ModNode

	// MovedTo is only filled for ghosts that were the source
	// of a move. It's the path of the node it was moved to.
	MovedTo string

	// WasPreviouslyAt points to the place `Curr` was at
	// before a move. On changes without a move this is empty.
	WasPreviouslyAt string
}

Change represents a single change of a node between two commits.

func CombineChanges

func CombineChanges(changes []*Change) *Change

CombineChanges compresses a list of changes (in a lossy way) to one Change. The one change should be enough to re-create the changes that were made.

func History

func History(lkr *c.Linker, nd n.ModNode, start, stop *n.Commit) ([]*Change, error)

History returns a list of `nd`'s states starting with the commit in `start` and stopping at `stop`. `stop` can be nil; in this case all commits will be iterated. The returned list has the most recent change upfront, and the latest change as last element.

func (*Change) FromCapnp

func (ch *Change) FromCapnp(msg *capnp.Message) error

FromCapnp deserializes `msg` and writes it to `ch`.

func (*Change) Replay

func (ch *Change) Replay(lkr *c.Linker) error

Replay applies the change `ch` onto `lkr` by redoing the same operations: move, remove, modify, add. Commits are not replayed, everything happens in lkr.Status() without creating a new commit.

func (*Change) String

func (ch *Change) String() string

func (*Change) ToCapnp

func (ch *Change) ToCapnp() (*capnp.Message, error)

ToCapnp converts a change to a capnproto message.

type ChangeType

type ChangeType uint8

ChangeType is a mask of possible state change events.

func (ChangeType) IsCompatible

func (ct ChangeType) IsCompatible(ot ChangeType) bool

IsCompatible checks if two change masks are compatible. Changes are compatible when they can be both applied without loosing any content. We may loose metadata though, e.g. when one side was moved, but the other removed: Here the remove would win and no move is counted.

func (ChangeType) String

func (ct ChangeType) String() string

String will convert a ChangeType to a human readable form

type ConflictStrategy

type ConflictStrategy int

ConflictStrategy defines what conflict strategy to apply in case of nodes with different content hashes.

func ConflictStrategyFromString

func ConflictStrategyFromString(spec string) ConflictStrategy

ConflictStrategyFromString converts a string to a ConflictStrategy. It it is not valid, ConflictStragetyUnknown is returned.

func (ConflictStrategy) String

func (cs ConflictStrategy) String() string

type Diff

type Diff struct {

	// Nodes that were added from remote.
	Added []n.ModNode

	// Nodes that were removed on remote side.
	Removed []n.ModNode

	// Nodes (of us) that are missing on the remote side.
	Missing []n.ModNode

	// Nodes from remote that were ignored.
	Ignored []n.ModNode

	// Nodes that were only moved (but nothing else)
	Moved []DiffPair

	// Merged contains nodes where sync is able to combine changes
	// on both sides (i.e. one side moved, another modified)
	Merged []DiffPair

	// Conflict contains nodes where sync was not able to combine
	// the changes made on both sides.
	Conflict []DiffPair
	// contains filtered or unexported fields
}

Diff describes a difference between two commits.

func MakeDiff

func MakeDiff(lkrSrc, lkrDst *c.Linker, headSrc, headDst *n.Commit, cfg *SyncOptions) (*Diff, error)

MakeDiff show the differences between two linkers.

Internally it works like Sync() but does not modify anything and just merely records what the algorithm decided to do.

type DiffPair

type DiffPair struct {
	Src     n.ModNode
	Dst     n.ModNode
	SrcMask ChangeType
	DstMask ChangeType
}

DiffPair is a pair of nodes that have a relation in regard of a change. The change is described by the masks.

type HistoryWalker

type HistoryWalker struct {
	// contains filtered or unexported fields
}

HistoryWalker provides a way to iterate over all changes a single Node had. It is capable of tracking a file even over multiple moves.

The API is loosely modeled after bufio.Scanner and can be used like this:

head, _ := lkr.Head()
nd, _ := lkr.LookupFile("/x")
walker := NewHistoryWalker(lkr, head, nd)
for walker.Next() {
    walker.Change()
}

if err := walker.Error(); err != nil {
    // Handle errors.
}

func NewHistoryWalker

func NewHistoryWalker(lkr *c.Linker, cmt *n.Commit, node n.ModNode) *HistoryWalker

NewHistoryWalker will return a new HistoryWalker that will yield changes of `node` starting from the state in `cmt` until the root commit if desired. Note that it is not checked that `node` is actually part of `cmt`.

func (*HistoryWalker) Err

func (hw *HistoryWalker) Err() error

Err returns the last happened error or nil if none.

func (*HistoryWalker) Next

func (hw *HistoryWalker) Next() bool

Next advances the walker to the next commit. Call State() to get the current state after. If there are no commits left or an error happened, false is returned. True otherwise. You should check after a failing Next() if an error happened via Err()

func (*HistoryWalker) State

func (hw *HistoryWalker) State() *Change

State returns the current change state. Note that the change may have ChangeTypeNone as Mask if nothing changed. If you only want states where it actually changed, just filter those.

type MapPair

type MapPair struct {
	Src n.ModNode
	Dst n.ModNode

	SrcWasRemoved bool
	SrcWasMoved   bool
	TypeMismatch  bool
}

MapPair is a pair of nodes (a file or a directory) One of Src and Dst might be nil: - If Src is nil, the node was removed on the remote side. - If Dst is nil, the node was added on the remote side.

Both shall never be nil at the same time.

If TypeMismatch is true, nodes have a different type and need conflict resolution.

If SrcWasRemoved is true, the node was deleted on the remote's side and we might need to propagate this remove. Otherwise, if src is nil, dst can be considered as missing file on src's side.

If SrcWasMoved is true, the two nodes were purely moved, but not modified otherwise.

type Mapper

type Mapper struct {
	// contains filtered or unexported fields
}

Mapper holds the state for the mapping algorithm.

func NewMapper

func NewMapper(lkrSrc, lkrDst *c.Linker, srcHead, dstHead *n.Commit, srcRoot n.Node) (*Mapper, error)

NewMapper creates a new mapper object that is capable of finding pairs of nodes between lkrDst and lkrSrc.

func (*Mapper) Map

func (ma *Mapper) Map(fn func(pair MapPair) error) error

Map calls `fn` for each pairing that was found. Equal files and directories are not reported. Most directories are also not reported, but if they are empty and not present on our side they will. No ghosts will be reported.

Some implementation background for the curious reader:

In the simplest case a filesystem is a tree and the assumption can be made that one node that lives at the same path on both sides is the same "file" (i.e. in terms of "this is the file that the user wants to synchronize with").

With ghosts though, we have nodes that can indicate a removed or a moved file. Due to moved files the filesystem tree becomes a graph and the mapping algorithm (that is the base of Mapper) needs to do a depth first search and thus needs to remember already visited nodes.

Since moved nodes also takes priorty we need to iterate over all ghosts first, and mark their respective counterparts or report that they were removed on the remote side (i.e. no counterpart exists.). Only after that we cycle through all other nodes and assume that files living at the same path reference the same "file". At this point we can treat the file graph as tree again by ignoring all ghosts.

A special case is when a file was moved on one side but, a file exists already on the other side. In this case the already existing files wins.

Some examples of the described behaviours can be found in the tests of Mapper.

type Patch

type Patch struct {
	FromIndex int64
	CurrIndex int64
	Changes   []*Change
}

Patch is a set of changes that changed since a certain version of a graph.

func MakePatch

func MakePatch(lkr *c.Linker, from *n.Commit, prefixes []string) (*Patch, error)

MakePatch creates a patch with all changes starting from `from`. It will only include nodes that are located under one of the prefixes in `prefixes`.

func (*Patch) FromCapnp

func (p *Patch) FromCapnp(msg *capnp.Message) error

FromCapnp deserializes `msg` into `p`.

func (*Patch) Len added in v0.2.0

func (p *Patch) Len() int

Len returns the number of changes in the patch.

func (*Patch) Less added in v0.2.0

func (p *Patch) Less(i, j int) bool

func (*Patch) Swap added in v0.2.0

func (p *Patch) Swap(i, j int)

func (*Patch) ToCapnp

func (p *Patch) ToCapnp() (*capnp.Message, error)

ToCapnp serializes a patch to capnproto message.

type SyncOptions

type SyncOptions struct {
	ConflictStrategy ConflictStrategy
	IgnoreDeletes    bool
	IgnoreMoves      bool

	OnAdd      func(newNd n.ModNode) bool
	OnRemove   func(oldNd n.ModNode) bool
	OnMerge    func(src, dst n.ModNode) bool
	OnConflict func(src, dst n.ModNode) bool
}

SyncOptions gives you the possibility to configure the sync algorithm.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL