Streams

This chapter describes concepts and procedures related to streams.

Introduction

Note

Be sure to read the “Basic Concepts” chapter of Introducing Helix before reading this chapter.

Streams are like branches, with additional intelligence built in. They provide clues of where and how to do branching and merging. They guide merging and branching actions that support both stability and innovation by encouraging the "`merge down, copy up'" best practice. In addition, using streams eliminates a lot of the work needed to define branches, to create workspaces, and to manage integrations.

Custom branching gives you finer grained control but you lose the convenience of built-in control over the flow of change and automatic workspace updating.

When you create a stream, you specify its type, information about the files it is associated with, its relationship to other streams, and how files are to be treated for branching and merging. The system uses the information you provide to encourage merging best practices and to track parallel development.

The stream type tells the system how stable the stream is relative to other streams. The stream’s path info tells the system a number of things; including which files to populate the workspace with, which files child streams are allowed to branch, and, if necessary, which changelist to lock the files at. Parent labeling specifies how the stream relates to other streams in the system, helping to determine how change flows through the system.

Streams are ideal for implementing the mainline branching model, in which less stable streams merge changes to keep up to date with their parents, then copy work to the parent when the work is stable enough to promote. In addition, streams enable the system to generate views for associated workspaces, eliminating the need for you to update views manually to reflect changes to your stream structure.

Stream workflow

This section walks you through the initial workflow for using streams.

  1. Create a stream depot.

    A stream depot contains one or more streams. Typically, a tech lead or administrator creates the stream depot. The syntax to create a stream depot is described in Create a stream depot. Stream depots are discussed in further detail in Stream depots.

  2. Create a mainline stream.

    A mainline stream resides at the center of your stream hierarchy. Typically, the mainline is a fairly stable receiving trunk, accepting development work from child streams and propagating the results to release streams where the work can be can be stabilized and built for release without impeding ongoing development. Typically, a tech lead or administrator creates a mainline stream. The syntax to create a mainline stream is described in Create a mainline stream.

    When you create a stream, the server creates the stream’s corresponding stream specification (spec), which defines the stream’s characteristics. The stream spec is examined in detail in The stream specification. You fine-tune the stream spec at the time you create a workspace, when the server prompts you to edit the stream spec. You can also update the stream’s characteristics as needed, as described in Updating streams.

  3. Create a workspace and bind it to the stream.

    To do any work in a stream you need a workspace. The syntax to create a workspace and bind it to a stream is discussed in Create a workspace. For background information on workspaces, see Stream workspaces.

  4. Populate the mainline stream.

    This step adds files to the mainline stream created earlier. Typically, a tech lead or administrator populates a mainline stream. Once you’ve populated a mainline stream, you can use standard server commands to modify and submit files. For details on how to populate a mainline stream, see Populate a mainline stream.

  5. Populate child streams.

    This step adds stream(s) that are children of the mainline stream. Typically, an individual contributor, such as a developer, creates and populates child streams. Once you’ve populated a child stream, you can use standard server commands to modify and submit files. For details on how to populate child streams, see Populate child streams.

  6. Make changes to files in one or more streams.

    These changes will be propagated in the next step.

  7. Propagate changes between streams.

    This step merges or copies files between different streams. For example, you may merge changes from a mainline stream into a child development stream. Typically, an individual contributor, such as a developer, propagates changes between streams. For details on how to propagate changes between streams, see Propagate changes.

Stream procedures

This section provides instructions on how to perform common stream-related procedures. For conceptual information on streams, see Key streams concepts.

As summarized in Stream workflow, to work with streams, you perform the following steps:

  1. Create a stream depot.
  2. Create a mainline stream.
  3. Create a workspace and bind it to the stream
  4. Populate the mainline stream.
  5. Populate child streams.
  6. Make changes to files in one or more streams
  7. Propagate changes between streams.

Create a stream depot

To create a depot, you must have super privilege. To create a stream depot:

  1. Issue the p4 depot depotname command. The depot specification form is displayed.
  2. Set the Type: field to stream.
  3. Adjust other settings as desired and save the specification.

Note that you cannot modify the type of a depot after you create it.

Create a mainline stream

To create a mainline stream:

  1. Issue the p4 stream, command, specifying the depot followed by the stream name.

    For example:

    $ p4  stream -t mainline //projectX/main

    The stream specification form is displayed.

  2. Change options in the spec to assign the stream the desired characteristics and save the spec.
  3. To verify that your mainline stream has been created, issue the p4 streams command.

    For example:

    $ p4  streams //projectX/...

Create a workspace

Before you can work in a stream, you must create a workspace associated with the stream. When you associate a workspace with a stream, Helix generates the workspace view based on the structure of the stream. You never need to edit the workspace view (and, in fact, cannot manually alter it). If the structure of the stream changes, Helix updates the views of workspaces associated with the stream on an as-needed basis.

Tip

When assigning names to stream-associated workspaces, adopt a naming convention such as user_depot_streamname. For example, bruno_projectX. If you regularly switch between client workspaces associated with different types of streams, you may also find it useful to append the stream type, to your workspace name, for example, bruno_projectX_main and bruno_projectX_dev.

To create a workspace for a stream:

  1. Issue the p4 client command, using the -S option to specify the name of the associated stream.

    For example:

    $ p4 client -S //projectX/main bruno_projectX

    The workspace specification form is displayed. (Note the Stream: field, which is present only for stream-associated workspaces.)

  2. Configure the workspace root directory and any other desired settings, and save the specification.

    You do not need to change the View: because this field is maintained by the server.

  3. Verify that your workspace has been created using p4 clients.

    For example:

    $ p4 clients -S //projectX/main

Now you can populate the mainline with files, as described in the next step.

Populate a mainline stream

There are two ways to populate a mainline stream:

  • Add files from the local filesystem
  • Branch files from another depot

If you need to preserve file history, branch the source files to the mainline stream. If you have no requirement for preserving file history, simply add them. The sections that follow describe each approach.

Add files

If you do not need to preserve the historic connection between the source files and the files in the new mainline stream, simply add them. To add files to the mainline stream:

  1. Create the workspace root directory if it does not exist.

    For example:

    C:\bruno_ws> cd C:\Users\bruno\p4clients
    C:\Users\bruno\p4clients> mkdir bruno_projectX_main
  2. Copy the files and folders to the workspace root directory.
  3. Change into the client workspace root directory, and use the p4 reconcile command to detect files not under Helix control and open them for add.

    C:\Users\bruno\p4clients> cd bruno_projectX_main
    C:\Users\bruno\p4clients\bruno_projectX_main> p4 add ...

To verify that the files are set up to be added correctly, issue the p4 opened command. To populate the stream, submit the changelist in which the files are open.

Branch from other depots

You can branch files from other stream depots, classic depots, or remote depots into a stream. If you populate the mainline by branching, Helix preserves the connection between the revision history of the source and target files. Your workspace must be set to one associated with the target stream (example: p4 set P4CLIENT=bruno_projectX_main).

To populate the mainline by branching, issue the p4 copy command, specifying source and target. Example:

$ p4 copy -v //mysourcedepot/mainline/... //ProjectX/main/...

In this example the -v option performs the copy on the server without syncing the newly-created files to the workspace. This can be a significant time-saver if there are many files being copied; you can then sync only the files you intend to work with from the new location.

p4d displays a series of “import from” messages listing the source and target files, and opens the file(s) in a pending changelist. To preview the results of the operation without opening files, specify the -n option. To undo an erroneous copy operation, issue the p4 revert command; for example:

$ p4 revert //ProjectX/main/...

Helix displays the stream specification with the type set to development. Save the specification and exit the editor to create the stream. To populate the stream with the files from the mainline, issue the following commands:

  1. To verify that the files are set up to be added correctly, issue the p4 opened command.
  2. To populate the stream, p4 submit the changelist in which the files are open.

If you are populating an empty stream, you can simplify this process by using p4 populate. For example:

$ p4 populate //mysourcedepot/mainline/...  //ProjectX/main/...

does the same thing as p4 copy -v followed by a p4 submit. If you are unsure of the results of p4 populate, use p4 populate -n, which previews the result of the command.

Populate child streams

After populating the mainline, you can branch files for development and for release. For example, to create a development stream that is a clone of its mainline parent, issue the following command:

$ p4 stream -t development -P //projectX/main //projectX/dev

Helix displays the stream specification with the type set to development. Save the specification. To populate the stream with the files from the mainline, issue the following commands:

$ p4 populate -d "From main" -S //projectX/dev -r
$ p4 sync

Propagate changes

Streams enable you to isolate stable code from work in progress, and to work concurrently on various projects without impediment. Best practice is to periodically update less stable streams from streams that are more stable (by merging), then promote changes to the more stable stream (by copying). Merging and copying are streamlined forms of integration. In general, propagate change as follows:

  • For copying and branching, use p4 copy or p4 populate.
  • For merging, use p4 merge.
  • For edge cases not addressed by p4 merge or p4 copy, use p4 integrate.

The preceding guidelines apply both to streams and to classic depots.

Comparing changes between streams

Using the p4 interchanges command, you can compare changes between streams to look for outstanding merges. Suppose you have a mainline stream //stream/main and its child, a development stream, //stream/dev. The following command tells you which changes exist in //stream/dev but not in its parent stream:

$ p4 interchanges -S //stream/dev

The following command tells you which changes exist in the parent of //stream/dev but not in //stream/dev:

$ p4 interchanges -S -r //stream/dev

Merging changes from a more stable stream

To update a stream with changes from a more stable stream, issue the p4 merge -S source-stream command, resolve as required, and submit the resulting changelist. By default, you cannot copy changes to a more stable stream until you have merged any incoming changes from the intended target. This practice ensures that you do not inadvertently overwrite any of the contents of the more stable stream.

Assuming changes have been checked into the mainline after you started working in the development stream (and assuming your workspace is set to a development stream), you can incorporate the changes into the development stream by issuing the following commands:

$ p4 merge
$ p4 resolve
$ p4 submit -d "Merged latest changes"

Copying changes to a more stable stream

After merging, your stream is up to date with its more stable parent or child. Assuming you’ve finalized the changes you want to make in the development stream, you can now promote its new content with no danger of overwriting work in the target stream. The copy operation simply propagates a duplicate of the source to the target, with no resolve required. For example, (and assuming your workspace is set to a mainline parent stream) to promote changes from the development stream to its parent mainline, issue the following commands:

$ p4 copy --from //projectX/dev
$ p4 submit -d "Check my new feature in"

Propagating change across the stream hierarchy

You might need to propagate a specific change between two streams that do not have a natural parent-child relationship, for example, to obtain an in-progress feature or bug fix from a peer development stream. To merge from or copy to such a stream, you can re-parent your stream by editing its specification and setting the Parent field to the desired source or target. This practice is not considered optimal but might be necessary. Alternatively, you can use the -P option with the p4 merge command to do a one-off integration between streams.

Key streams concepts

This section provides further information on key streams concepts, including:

  • The stream specification
  • Updating streams
  • Stream types
  • Stream paths
  • Stream workspaces
  • Stream depots

The stream specification

A stream spec names a path in a stream depot to be treated as a stream. A spec defines the stream’s location, its type, its parent stream, the files in its view, and other configurable behaviors. It is created when you create a stream with the p4 stream command. You can update the spec’s entries — as described in Updating streams — to change the stream’s characteristics.

The following is a sample stream spec:

$ p4 stream -o //stream/child_of_main
# A Perforce Stream Specification.
#
# Use *'p4 help stream'* to see more about stream specifications and command.

Stream: //stream/child_of_main

Update: 2015/02/06 10:57:04

Access: 2015/02/06 10:57:04

Owner:  jschaffer

Name:   //stream/child_of_main (created by switch command)

Parent: //stream/main

Type:   development

Options:        allsubmit unlocked toparent fromparent mergeany

Description:
        Our primary development stream for the project.

Paths:
        share ...
        import boost/... //3rd_party/boost/1.53.0/artifacts/original/...
        import boost/lib/linux26x86_64/... //3rd_party/boost/1.53.0/artifacts/original/lib/linuxx86_64/gcc44libc212/...
        import boost/lib/linux26x86/... //3rd_party/boost/1.53.0/artifacts/original/lib/linuxx86/gcc44libc212/...
        import protobuf/... //3rd_party/protobuf/2.4.1/artifacts/patch-1/...
        import gtest/... //3rd_party/gtest/1.7.0/artifacts/original/...
        import icu/... //3rd_party/icu/53.1/artifacts/original/...
        import p4-bin/lib.ntx64/vs11/p4api_vs2012_dyn.zip //builds/p15.1/p4-bin/bin.ntx64/p4api_vs2012_dyn.zip
        import p4/... //depot/p15.1/p4/...
        exclude p4/Jamrules
        exclude p4/lbr/...
        exclude p4/server/...

Remapped:
        p4/doc/... p4/relnotes/...

Ignored:
        .../~tmp.txt

The following table describes the stream spec in more detail:

Entry Meaning

Stream

The Stream field is unique and specifies the depot path where the stream files live. All streams in a single stream depot must have the same number of forward slashes in their name; your administrator specifies this number in the StreamDepth field of the stream depot spec. If you try to create a stream with a different number of forward slashes than those specified in the StreamDepth field, you’ll get an error message like the following:

Error in stream specification. Stream streamname does not reflect depot
depth-field streamdepth.

Check with your administrator to determine the permitted stream depth.

Update

The date the stream specification was last changed.

Access

The date the specification was originally created.

Owner

The user or group who has specific and unique permissions to access to this stream.

Name

An alternate name of the stream, for use in display outputs. Defaults to the streamname portion of the stream path.

Parent

The parent of this stream. Can be none if the stream type is mainline, otherwise must be set to an existing stream identifier, of the form //depotname/streamname.

Type

Type of stream provides clues for commands run between stream and parent. The five types include mainline, release, development (default), virtual and task.

Description

A short description of the stream (optional).

Options

Stream Options: allsubmit/ownersubmit [un]locked [no]toparent [no]fromparent mergedown/mergeany

Paths

Identify paths in the stream and how they are to be generated in resulting workspace views of this stream. Path types are share/isolate/import/import+/exclude, which are discussed further in Stream paths. p4d uses the Paths entry to generate a workspace view. See Stream workspaces.

Note

Files don’t actually have to be branched to appear in a stream. Instead, they can be imported from the parent stream or from other streams in the system.

Remapped

Remap a stream path in the resulting workspace view.

Ignored

Ignore a stream path in the resulting workspace view. Note that Perforce recommends that you use p4 ignore in lieu of this entry, to accomplish the same thing.

More on options

The following table summarizes the meaning of each of the options available in the stream spec:

Option Meaning

allsubmit

All users can submit changes to the stream.

ownersubmit

Only the stream owner can submit changes to the stream.

locked

The stream spec cannot be deleted and only the stream owner can modify it.

unlocked

All users can edit or delete the stream spec.

toparent

Merges from the stream to its parent are expected.

notoparent

Merges from the stream to the parent are not expected.

fromparent

Merges to the stream from the parent are expected.

nofromparent

Merges to the stream from the parent are not expected.

mergedown

Enforces the best practice of merge down, copy up.

mergeany

Allows you to merge the stream’s content both up and down.

Updating streams

As part of maintaining your version control application, you will likely update streams over time, by changing any of the fields listed above, to do such things as:

  • modify the paths the stream consumes when the stream proves to be too narrow or too wide, in order to:

    • change the version of an included library by modifying the target of an import path
    • change the scope of a path to widen or narrow the scope included
  • Change restrictions on who can submit to the stream

To do this, you modify stream specifications directly via the p4 stream command, automatically and immediately updating all workspace views derived from that stream.

Making changes to a stream spec and associated files atomically

Alternatively, you can isolate edits to the stream spec to the editing client prior to making them available to other clients as part of an atomic changelist submission. This works just as edits to files do: they are made locally on a single client and then submitted to make them available to other clients.

This functionality has a couple of important benefits:

  • You can stage a stream spec in your workspace and test it before submitting it.
  • You can submit the spec atomically in a changelist along with a set of files. Since the stream structure dictates the workspace view, this means that when users sync, they obtain the new view and the new files together.

You open and submit changes to the stream spec using the following three commands:

  • p4 stream edit puts the client’s current stream spec into the opened state, isolating any edits made to fields that affect view generation. While the spec is open, those fields are marked with the comment #open to indicate that they are open and isolated to your client. Changes made to these fields affect your workspace view normally, but other clients are not affected.
  • p4 stream resolve resolves changes that have been submitted to the stream spec by other users since you opened it. You may not submit changes to the stream spec until newer changes have been resolved.
  • p4 stream revert reverts any pending changes made to the open spec, returning your client to the latest submitted version of the stream.

For details on all three of these commands, see the p4 stream page in the P4 Command Reference.

By default, the open stream spec is included along with files that are shelved or submitted in a changelist. Conversely, when unshelving a change that contains an open stream spec, the current stream is opened and the shelved version becomes the opened version. If the stream is already open when attempting to unshelve, a warning is generated and the unshelve operation aborts. The stream may be omitted from any of these operations by using the -Af flag to specify that only files should be acted upon.

See the p4 submit, p4 shelve, and p4 unshelve commands in the P4 Command Reference for details.

Stream types

You assign stream types according to the stream’s expected usage, stability and flow of change:

  • Development streams are used for code that changes frequently; they they enable you to experiment without destabilizing the mainline stream.
  • Mainline streams contain code that changes somewhat frequently, but is more stable than code in development streams.
  • Release streams contain the most stable code, as this is the code closest to being released. Release streams enable you to finalize existing features while working on new features in the mainline.

There is also a virtual stream type and a task stream type. See Task streams and Virtual streams, respectively.

On a scale of stability, a development stream is considered less stable than its mainline stream parent, while a release stream is considered more stable than its mainline stream parent. Change is expected to flow down by merging, and up by copying. This “merge down, copy up” practice assures that merging is done only when necessary, and always in the more forgiving of the two streams involved.

Merging means incorporating another stream’s changes into your stream, and can require you to resolve conflicts. Copy propagates a duplicate of the source stream to the target. The following diagram shows a basic stream hierarchy: changes are merged down (to streams of lesser stability) and copied up (to streams of greater stability):

stream graph

The following table summarizes these qualities of stream types:

Stream Type Stability Merge Copy

mainline

Stable per your policy (for example, all code builds)

from child (from release, or to development)

to child (to release, or from development)

virtual

N/A; used to filter streams

N/A

N/A

development

Unstable

from parent

to parent

task

Unstable

from parent

to parent

release

Highly stable

to parent

from parent

Task streams

Task streams are lightweight short-lived streams used for bug fixing or new features that only modify a small subset of the stream data. Since branched (copied) files are tracked in a set of shadow tables that are later removed, repository metadata is kept to a minimum when using this type of stream and server performance is optimized.

They are branches that work just like development streams, but task streams remain semi-private until branched back to the parent stream. Designed as lightweight branches, they are most effective when anticipated work in the branch will only affect a small number of files relative to the number of files in the branch.

Task streams are intended to be deleted or unloaded after use. Because you cannot re-use task stream names even after the stream has been deleted, most sites adopt a naming convention that is likely to be unique for each task, such as user-date-jobnumber.

Working within task streams is just like working in a development stream:

  1. Create the task stream (in this example, as a child of a development stream).

    $ p4 stream -t task -P //projectX/dev //Tasks/mybug123
  2. Populate the stream.

    $ p4 populate -d "Fix bug 123" -S //Tasks/mybug123 -r
  3. Make changes to files in the stream and submit the changes.
  4. Merge down any required changes from the parent stream, resolving as necessary.

    $ p4 merge
  5. Copy up the changes you made into the parent stream.

    $ p4 copy --from //Tasks/mybug123
  6. Delete or unload the task stream.

    $ p4 stream -d //Tasks/mybug123

    Alternatively, use:

    $ p4 unload -s //Tasks/mybug123

    to unload it. Use unload if you think you might to work on the task stream again.

Only workspaces associated with the task stream can see all the files in the stream; the stream appears as a sparse branch to other workspaces, which see only those files and revisions that you changed within the task stream. Most other metadata for the task stream remains private.

Task streams can quickly accumulate in a depot until they are deleted or unloaded; to keep a project depot uncluttered by task streams, your Helix administrator or project lead may choose to establish certain streams depots as dedicated holding areas for task streams. In this case, create your stream in the task streams depot as a child of a parent in the project depot.

Task streams are unique in that they can live in different depots from their children or parents. However, the best practice is to have them reside in the same depot as their children or parents.

Virtual streams

Virtual streams can be used to create alternative views of real streams. Virtual streams differ from other stream types in that a virtual stream is not a separate set of files, but instead a filtered view of its parent stream. A virtual stream can have child streams, and its child streams inherit its views.

Stream paths

Stream paths control the files and paths that compose a stream and define how those files are propagated. Except for the mainline, each stream inherits its structure from its parent stream. To modify the structure of the child, you specify the paths as follows:

Type Sync? Submit? Integrate to/from Parent? Remarks
share

Y

Y

Y

(Default) For files that are edited and propagated between parent and child streams. All files in a shared path are branched and, in general, shared paths are the least restricted.

isolate

Y

Y

N

For files that must not be propagated outside the stream but can be edited within it, such as binary build results.

import

Y

N

N

For files that must be physically present in the stream but are never changed. Example: third-party libraries. Import paths can reference a specific changelist (or a label that aliases a changelist) to limit the imported files to the revisions at that change or lower. Use the syntax @changelist#, as in: //depot/lib3.0/…​@455678.

import+

Y

Y

N

Functions like an import path, in that it can reference an explicitly-defined depot path, but unlike a standard import path, you can submit changes to the files in an import+ path.

exclude

N

N

N

Files in the parent stream that must never be part of the child stream.

In the following example, files in the src path are not submittable (and are imported from the parent stream’s view), files in the lib path are not submittable (and are imported from an explicitly-specified location in the depot), and files in the db path can be edited and submitted in the stream, but can never be copied to the parent:

Paths:
        share ...
        import src/...
        import lib/... //depot/lib3.0/...
        isolate db/...

The paths are used to generate the mappings for workspaces that are associated with the stream. If the stream structure changes, the workspace views are updated automatically and in fact cannot be altered manually. If the stream is locked, only the stream owner (or stream owners, if the Owner: field of the stream is set to a group) can edit the stream specification.

Stream specification can also remap file locations (so that a file in specified depot location is synced to a different location in the workspace) and screen out files according to file type. For example, to ensure that object files and executables are not part of the stream, add the following entries to the stream specification:

Ignored:
        .o
        .exe

Stream paths and inheritance between parents and children

Child streams inherit folder paths and behavioral rules from their parents. When we talk about inheritance between parents and children, it helps to think in the following terms:

  • Permissiveness: what actions (submit, sync, etcetera) are permitted on a path?

    Path types are inherited from parent streams, and you cannot override the effects of the path types assigned by parent streams. In other words, child streams are always as permissive or less permissive than their parents, but never more permissive. For example, if a parent stream defines a path as isolate, its child streams cannot redefine the path as share to enable integrations.

  • Inclusiveness: what paths are included in the stream?

    Since children cannot, by definition, be more inclusive than their parents, you cannot include a folder path in a child that is not also included in its parent. This means, for example, that you cannot add an isolate path to a child if the folders in that path are not also included in the parent.

    In the example in the table below, the incorrectly defined Dev stream, which is a child of Main, contains an isolate path that does not work, because it includes folders that are not included in the parent. In order to isolate the config/ folder in the Dev stream, that folder has to be included as a share or isolate path in Main:

    Incorrect Correct
    Stream: //Acme/Main
    Parent: none
    Paths: share apps/...
    Paths: share tests/...
    
    
    Stream: //Acme/Dev
    Parent: //Acme/Main
    Paths: share apps/...
           share tests/...
           isolate config/...
    Stream: //Acme/Main
    Parent: none
    Paths: share apps/...
           share tests/...
           share config/...
    
    Stream: //Acme/Dev
    Parent: //Acme/Main
    Paths: share apps/...
           share tests/...
           isolate config/...

Example 34. Simple share

Let’s start with a simple case: two streams, //Ace/main and its child //Ace/dev.

Stream: //Ace/main
Parent: none
Paths:  share ...

Stream: //Ace/dev
Parent: //Ace/main
Paths:  share ...

In this case, the entire stream path is shared. When you switch your workspace to the //Ace/main stream, the workspace view looks like this:

//Ace/main/... //your_ws/...

The workspace view maps the root of the //Ace/main stream to your workspace. When you you switch your workspace to the //Ace/dev stream, the workspace view is this:

//Ace/dev/... //your_ws/...

And the branch view for //Ace/dev/ looks like this:

//Ace/dev/... //Ace/main/...

In other words, the entire dev stream can be synced to workspaces, and the entire stream can be branched, merged, and copied.

Example 35. Share and import

Let’s look at an example where software components are housed in three separate depots: //Acme, //Red, and //Tango.

The Acme mainline is configured like this:

Stream: //Acme/Main
Parent: none
Paths:  share apps/...
        share tests/...
        import stuff/... //Red/R6.1/stuff/...
        import tools/... //Tango/tools/...

If you switch your workspace to the //Acme/Main stream, this would be your workspace view:

//Acme/Main/apps/...  //your_ws/apps/...
//Acme/Main/tests/... //your_ws/tests/...
//Red/R6.1/stuff/...  //your_ws/stuff/...
//Tango/tools/...     //your_ws/tools/...

The stream’s Paths field lists folders relative to the root of the stream. Those are the folders you get in your workspace, beneath your workspace root. The shared folders are mapped to the //Acme/Main path, and the imported paths are mapped to their locations in the //Red and //Tango depots.

Example 36. Share, isolate, exclude, and import

Let’s say that your team doesn’t want to do actual development in the mainline. In this example, XProd feature team has a development stream of their own, defined like this:

Stream: //Acme/XProd
Parent: //Acme/Main
Paths:  import ...
        isolate apps/bin/...
        share apps/xp/...
        exclude tests/...

Switching your workspace to the //Acme/XProd stream gives you this view:

//Acme/Main/apps/...      //your_ws/apps/...
//Acme/XProd/apps/bin/... //your_ws/apps/bin/...
//Acme/XProd/apps/xp/...  //your_ws/apps/xp/...
//Red/R6.1/stuff/...      //your_ws/stuff/...
//Tango/tools/...         //your_ws/tools/...
-//Acme/XProd/tests/...   //your_ws/tests/...

Here we see workspace view inheritance at work. The contents of imported paths are mapped into your workspace. The shared and isolated paths are mapped to the child stream; these contain the files the XProd team is working on and will be submitting changes to. And the excluded path (marked with a minus sign in the view) doesn’t appear in the workspace at all.

Because the //Acme/XProd stream has a parent, it has a branch mapping that can be used by the copy and merge commands. That branch view consists of the following, with just one path shared by the child and parent.

Note

You must use the Perforce Command Line Client to view stream branch views.

-//Acme/XProd/apps/...     //Acme/Main/apps/...
-//Acme/XProd/apps/bin/... //Acme/Main/apps/bin/...
//Acme/XProd/apps/xp/...   //Acme/Main/apps/xp/...
-//Acme/XProd/stuff/...    //Acme/Main/stuff/...
-//Acme/XProd/tests/...    //Acme/Main/tests/...
-//Acme/XProd/tools/...    //Acme/Main/tools/...

When you work in an //Acme/XProd workspace, it feels as if you’re working in a full branch of //Acme/Main, but the actual branch is quite small.

Example 37. Child that shares all of the above parent

Let’s suppose that Lisa, for example, creates a child stream from //Acme/XProd. Her stream spec looks like this:

Stream: //Acme/LisaDev
Parent: //Acme/XProd
Paths:  share ...

Lisa’s stream has the default view template. Given that Lisa’s entire stream path is set to share, you might expect that her entire workspace will be mapped to her stream. But it is not, because inherited behaviors always take precedence; sharing applies only to paths that are shared in the parent as well. A workspace for Lisa’s stream, with its default view template, has this client view:

//Acme/Main/apps/...        //your_ws/apps/...
-//Acme/LisaDev/tests/...   //your_ws/tests/...
//Acme/LisaDev/apps/bin/... //your_ws/apps/bin/...
//Acme/LisaDev/apps/xp/...  //your_ws/apps/xp/...
//Red/R6.1/stuff/...        //your_ws/stuff/...
//Tango/tools/...           //your_ws/tools/...

A workspace in Lisa’s stream is the same as a workspace in the XProd stream, with one exception: the paths available for submit are rooted in //Acme/LisaDev. This makes sense; if you work in Lisa’s stream, you expect to submit changes to her stream. By contrast, the branch view that maps the //Acme/Dev stream to its parent maps only the path that is designated as shared in both streams:

-//Acme/Main/apps/...        //XProd/apps/...
-//Acme/LisaDev/tests/...    //XProd/tests/...
-//Acme/LisaDev/apps/bin/... //XProd/apps/bin/...
//Acme/LisaDev/apps/xp/...   //your_ws/apps/xp/...
-//Red/R6.1/stuff/...        //XProd/stuff/...
-//Tango/tools/...           //XProd/tools/...

The default template allows Lisa to branch her own versions of the paths her team is working on, and have a workspace with the identical view of non-branched files that she would have in the parent stream.

Stream workspaces

To submit files to a stream, you must use a workspace that is bound to that stream; such a workspace is known as a stream workspace. A stream workspace is bound to a stream by way of the Stream: field in the workspace spec. The paths listed in the stream spec determine which files appear in a workspace view.

With stream workspaces you don’t have to manually set up a workspace view; instead the system automatically generates the workspace view from the Paths: section of the stream spec. Thus, if you switch a workspace from one stream to another, or if you modify a stream’s view template, all workspaces bound to the stream update accordingly.

In order to submit changes to files in a stream, you must use a workspace bound to or associated with that stream. Opening a file outside of your stream-generated view for edit with p4 edit gives a warning that the file cannot be submitted.

For example, suppose your stream spec contains the following two entries under Paths::

Paths:
import ...

isolate apps/bin/...

share apps/xp/...

exclude tests/...

Switching your workspace to this stream gives you this workspace view:

//Acme/Main/apps/...      //your_ws/apps/...

//Acme/XProd/apps/bin/... //your_ws/apps/bin/...

//Acme/XProd/apps/xp/...  //your_ws/apps/xp/...

//Red/R6.1/stuff/...      //your_ws/stuff/...

//Tango/tools/...         //your_ws/tools/...

-//Acme/XProd/tests/...   //your_ws/tests/...

See Stream paths for more information on the relationship between paths listed in the stream spec and workspace (also known as client) views.

By default, p4 stream edits the stream associated with your current workspace. It throws an error if you’re not using a stream workspace.

Managing stream workspaces

This section discusses various approaches to managing your stream workspaces.

Using one workspace for multiple streams

When working with multiple streams, you have two choices:

  • Switch one workspace between multiple streams; the workspace is appropriately populated whenever you switch from one stream to another. While this requires some extra processing, it is the right choice when you don’t need to work on different streams at the same time and you don’t want to have multiple streams on disk at the same time.
  • Establish a distinct workspace for each stream. This is the right choice if you want to move quickly between different streams or if you want to have multiple streams on disk at the same time.

    Note that distinct workspaces must have distinct workspace roots — that is, distinct local folders.

To change the stream associated with a workspace, issue the following command:

$ p4 switch streamname

To get a workspace view and a set of files as of a specific changelist, issue the following command:

$ p4 switch stream@change

Narrowing the scope of workspaces with virtual streams

For large projects, even consistently-organized streams may not sufficiently restrict workspace views. In large organizations, there are often many groups who are concerned with only a small subset of a project’s files. In classic Helix, these users would manually restrict their workspace’s view to include only the desired subset. Streams offers an alternative; use a virtual stream as a filter:

For example, if ongoing development work is occurring in an //Ace/dev stream:

Stream:     //Ace/dev
Parent:     //Ace/main
Type:       development
Paths:
        share ...

Then a user who is working only with the documentation for the product (rather than all of the assets associated with the project) could create a virtual stream that includes only those files under //Ace/dev/docs/..., as follows:

Stream:     //Ace/devdocs
Parent:     //Ace/dev
Type:       virtual
Paths:
        share docs/...

The user can then can switch his or her workspace to the devdocs virtual stream with the following command:

$ p4 switch //Ace/devdocs

When using the devdocs workspace, the user’s workspace view is automatically updated to include only the material in //Ace/dev/docs/... and any changes he or she makes in //Ace/devdocs are automatically made directly in the original //Ace/dev codeline without the need to manually run p4 copy or p4 merge.

Viewing a stream as of a specific changelist

The StreamAtChange option in the workspace specification lets you use the version of the stream specified as of a particular changelist to generate a workspace view. This is helpful when you want to see what the stream view was at a particular point in time, especially if your stream spec changes a lot (for example, if you frequently change what you’re importing or what you’re deciding to share). When you use the StreamAtChange option, you cannot submit changes to the files in the stream, since your workspace view is not up to date.

To set a stream workspace to use the version of the stream specified as of a particular changelist, do the following:

  1. Open the stream’s workspace specification form for editing.

    $ p4 client
  2. Use one of the following alternatives:

    1. Edit the form to set StreamAtChange: to the changelist you want to view the stream as of. Or,
    2. Issue this command:

      $ p4 client -S //Ace/main@12546

For more information, see the P4 Command Reference.

Alternatively, you can issue the following command to sync a stream using the stream’s view as of a specific changelist:

$ p4 switch [-r -v] stream@change

This command both sets the StreamAtChange value and syncs to the same change.

Stream depots

Streams are rooted in stream depots. A mainline and all of the streams related to it are rooted in the same stream depot. A server can host multiple stream depots. Although a stream depot can have multiple mainlines, one mainline per stream depot is recommended. Stream depots exist as separate namespaces from classic depots so that users don’t mix stream and non-stream content.