vcs

package
v0.1.0-beta Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2018 License: AGPL-3.0 Imports: 10 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 = iota
	ConflictStragetyIgnore
	ConflictStragetyUnknown
)

Variables

View Source
var (
	DefaultSyncConfig = &SyncConfig{}
)

Functions

func ParentDirectoryForCommit

func ParentDirectoryForCommit(lkr *c.Linker, cmt *n.Commit, curr n.Node) (*n.Directory, error)

func ResetFile

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

ResetFile 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.

func Sync

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

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

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

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

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) String

func (ns *Change) String() string

type ChangeType

type ChangeType uint8

ChangeType is a mask of possible state change events.

func (ChangeType) IsCompatible

func (ct ChangeType) IsCompatible(ot ChangeType) bool

rule: do not loose content,

    but we may loose metadata.

|  a  c  r  m

--------------- a | n n n y c | n n n y r | y y y y m | y y y y

func (ChangeType) String

func (ct ChangeType) String() string

String will convert a ChangeType to a human readable form

type ConflictStrategy

type ConflictStrategy int

func ConflictStrategyFromString

func ConflictStrategyFromString(spec string) ConflictStrategy

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
}

func MakeDiff

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

Diff 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
}

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 walker.Error() != 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 happended, false is returned. True otherwise. You should check after a failing Next() if an error happended 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)

func (*Mapper) Map

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

Diff 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 SyncConfig

type SyncConfig 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
}

SyncConfig gives you the possibility to configure the sync algorithm.

Jump to

Keyboard shortcuts

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