Showing preview only (3,447K chars total). Download the full file or copy to clipboard to get everything.
Repository: pcottle/learnGitBranching
Branch: main
Commit: e6437a3da0df
Files: 156
Total size: 3.3 MB
Directory structure:
gitextract_ow2z9stj/
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── settings.yml
│ └── workflows/
│ ├── build-docker.yml
│ └── containerize.yml
├── .gitignore
├── .gitpod.yml
├── .jshintrc
├── .travis.yml
├── CLAUDE.md
├── CNAME
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.md
├── Makefile
├── README.md
├── __tests__/
│ ├── CommandLineStore.spec.js
│ ├── GlobalStateStore.spec.js
│ ├── LevelStore.spec.js
│ ├── LocaleStore.spec.js
│ ├── animation.spec.js
│ ├── base.js
│ ├── collections.spec.js
│ ├── commandModel.spec.js
│ ├── create.js
│ ├── errors.spec.js
│ ├── eventEmitter.spec.js
│ ├── git.spec.js
│ ├── levels.spec.js
│ ├── mercurial.spec.js
│ ├── remote.spec.js
│ ├── simpleRemote.spec.js
│ ├── treeCompare.spec.js
│ ├── vcs.spec.js
│ └── visuals.spec.js
├── checkgit.sh
├── generatedDocs/
│ ├── github-markdown.css
│ └── levels.html
├── gulpfile.js
├── package.json
├── scripts/
│ ├── check-package-manager.js
│ └── translate.js
├── src/
│ ├── js/
│ │ ├── actions/
│ │ │ ├── CommandLineActions.js
│ │ │ ├── GlobalStateActions.js
│ │ │ ├── LevelActions.js
│ │ │ └── LocaleActions.js
│ │ ├── app/
│ │ │ └── index.js
│ │ ├── commands/
│ │ │ └── index.js
│ │ ├── constants/
│ │ │ └── AppConstants.js
│ │ ├── dialogs/
│ │ │ ├── confirmShowSolution.js
│ │ │ ├── levelBuilder.js
│ │ │ ├── nextLevel.js
│ │ │ └── sandbox.js
│ │ ├── dispatcher/
│ │ │ └── AppDispatcher.js
│ │ ├── git/
│ │ │ ├── commands.js
│ │ │ ├── gitShim.js
│ │ │ ├── headless.js
│ │ │ └── index.js
│ │ ├── graph/
│ │ │ ├── index.js
│ │ │ └── treeCompare.js
│ │ ├── intl/
│ │ │ ├── checkStrings.js
│ │ │ ├── index.js
│ │ │ └── strings.js
│ │ ├── level/
│ │ │ ├── builder.js
│ │ │ ├── disabledMap.js
│ │ │ ├── index.js
│ │ │ └── parseWaterfall.js
│ │ ├── log/
│ │ │ └── index.js
│ │ ├── mercurial/
│ │ │ └── commands.js
│ │ ├── models/
│ │ │ ├── collections.js
│ │ │ └── commandModel.js
│ │ ├── react_views/
│ │ │ ├── CommandHistoryView.jsx
│ │ │ ├── CommandView.jsx
│ │ │ ├── CommandsHelperBarView.jsx
│ │ │ ├── HelperBarView.jsx
│ │ │ ├── IntlHelperBarView.jsx
│ │ │ ├── LevelToolbarView.jsx
│ │ │ └── MainHelperBarView.jsx
│ │ ├── sandbox/
│ │ │ ├── commands.js
│ │ │ └── index.js
│ │ ├── stores/
│ │ │ ├── CommandLineStore.js
│ │ │ ├── GlobalStateStore.js
│ │ │ ├── LevelStore.js
│ │ │ └── LocaleStore.js
│ │ ├── util/
│ │ │ ├── constants.js
│ │ │ ├── debounce.js
│ │ │ ├── debug.js
│ │ │ ├── errors.js
│ │ │ ├── escapeString.js
│ │ │ ├── eventBaton.js
│ │ │ ├── eventEmitter.js
│ │ │ ├── index.js
│ │ │ ├── keyMirror.js
│ │ │ ├── keyboard.js
│ │ │ ├── mock.js
│ │ │ ├── promise.js
│ │ │ ├── reactUtil.js
│ │ │ ├── throttle.js
│ │ │ └── zoomLevel.js
│ │ ├── views/
│ │ │ ├── builderViews.js
│ │ │ ├── commandViews.js
│ │ │ ├── gitDemonstrationView.js
│ │ │ ├── index.js
│ │ │ ├── levelDropdownView.js
│ │ │ ├── multiView.js
│ │ │ └── rebaseView.js
│ │ └── visuals/
│ │ ├── animation/
│ │ │ ├── animationFactory.js
│ │ │ └── index.js
│ │ ├── index.js
│ │ ├── tree.js
│ │ ├── visBase.js
│ │ ├── visBranch.js
│ │ ├── visEdge.js
│ │ ├── visNode.js
│ │ ├── visTag.js
│ │ └── visualization.js
│ ├── levels/
│ │ ├── advanced/
│ │ │ └── multipleParents.js
│ │ ├── index.js
│ │ ├── intro/
│ │ │ ├── branching.js
│ │ │ ├── commits.js
│ │ │ ├── merging.js
│ │ │ └── rebasing.js
│ │ ├── mixed/
│ │ │ ├── describe.js
│ │ │ ├── grabbingOneCommit.js
│ │ │ ├── jugglingCommits.js
│ │ │ ├── jugglingCommits2.js
│ │ │ └── tags.js
│ │ ├── rampup/
│ │ │ ├── cherryPick.js
│ │ │ ├── detachedHead.js
│ │ │ ├── interactiveRebase.js
│ │ │ ├── relativeRefs.js
│ │ │ ├── relativeRefs2.js
│ │ │ └── reversingChanges.js
│ │ ├── rebase/
│ │ │ ├── manyRebases.js
│ │ │ └── selectiveRebase.js
│ │ └── remote/
│ │ ├── clone.js
│ │ ├── fakeTeamwork.js
│ │ ├── fetch.js
│ │ ├── fetchArgs.js
│ │ ├── fetchRebase.js
│ │ ├── lockedMain.js
│ │ ├── mergeManyFeatures.js
│ │ ├── pull.js
│ │ ├── pullArgs.js
│ │ ├── push.js
│ │ ├── pushArgs.js
│ │ ├── pushArgs2.js
│ │ ├── pushManyFeatures.js
│ │ ├── remoteBranches.js
│ │ ├── sourceNothing.js
│ │ └── tracking.js
│ ├── style/
│ │ ├── font-awesome.css
│ │ ├── main.css
│ │ └── rainbows.css
│ └── template.index.html
└── vite.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# Top-most editorconfig file
root = true
[*]
indent_style = space
indent_size = 2
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [pcottle]
================================================
FILE: .github/settings.yml
================================================
repository:
description: An interactive git visualization and tutorial. Aspiring students of git can use this app to educate and challenge themselves towards mastery of git!
homepage: https://pcottle.github.io/learnGitBranching/
================================================
FILE: .github/workflows/build-docker.yml
================================================
name: Docker - learnGitBranching image
on:
workflow_dispatch:
push:
branches:
- main # Trigger CI on main branch
- bmcclure/main
paths:
- '**/*'
- '.github/workflows/build-docker.yml'
pull_request:
branches:
- main # Trigger gated pipeline on PR to main
- bmcclure/main
paths:
- '**/*'
- '.github/workflows/build-docker.yml'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
tags: |
type=ref,event=pr
type=ref,event=branch
type=sha,format=long
type=raw,value=latest
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build Docker image (non main branch)
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
if: github.ref != 'refs/heads/bmcclure/main'
with:
context: .
load: true
push: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Build and push Docker image (main branch)
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
if: github.ref == 'refs/heads/bmcclure/main'
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- id: lowercaseImageName
uses: ASzc/change-string-case-action@v2
with:
string: ${{ env.IMAGE_NAME }}
- name: Save Docker Image archive to local filesystem
run: "docker save --output learnGitBranching.tar ${{env.REGISTRY}}/${{ steps.lowercaseImageName.outputs.lowercase }}"
- name: Upload application's Docker Image as pipeline artifact
uses: actions/upload-artifact@v4
with:
path: learnGitBranching.tar
name: learnGitBranching.tar
================================================
FILE: .github/workflows/containerize.yml
================================================
name: containerize
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
env:
REPO_PREFIX: ghcr.io/${{ github.repository }}
steps:
- uses: actions/checkout@v4
- name: login-ghcr
run: echo ${{ secrets.GHCR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: build-docker-image
run: |
REPO_PREFIX_LOWER=$(echo "$REPO_PREFIX" | tr '[:upper:]' '[:lower:]')
docker build -t $REPO_PREFIX_LOWER:${{ github.sha }} -f Dockerfile .
docker push $REPO_PREFIX_LOWER:${{ github.sha }}
docker tag $REPO_PREFIX_LOWER:${{ github.sha }} $REPO_PREFIX_LOWER:latest
docker push $REPO_PREFIX_LOWER:latest
================================================
FILE: .gitignore
================================================
# NPM / Yarn / Pnpm
npm-debug.log
yarn-error.log
pnpm-lock.yaml
package-lock.json
node_modules/
# Build artifacts and asset stuff
build/*
screens
FontAwesome-Vectors.pdf
index.html
# Vim swaps
*.sw*
# sed backups
*.bak
# Annoying mac stuff
.DS_STORE
# Xcode
*.xcuserstate
*.xcworkspace
xcuserdata
UserInterfaceState.xcuserstate
*.pbxuser
*.perspective
*.perspectivev3
*.mode1v3
*.mode2v3
*.xcodeproj/xcuserdata/*.xcuserdatad
*.xccheckout
*.xcuserdatad
*.pyc
.User.xcconfig
Tools/xctool/build
Tools/clang/analyzer/build
Tools/clang/libtooling/build
Tools/clang/clang-ocaml/build
Tools/clang/xcode
VendorLib/Breakpad/src/tools/mac/dump_syms/build
DerivedData
VendorLib/clang/lib/arc
VendorLib/clang/lib/c++
.idea
# NYC test coverage output
.nyc_output/
================================================
FILE: .gitpod.yml
================================================
ports:
- port: 8000
onOpen: open-preview
tasks:
- init: yarn install
command: >
yarn gulp fastBuild &&
printf "\nWelcome to Learn Git Branching\nTo rebuild the app, simply run 'yarn gulp fastBuild' and reload index.html.\n\n" &&
python3 -m http.server 8000 2>/dev/null
================================================
FILE: .jshintrc
================================================
{
"esversion": 6,
"curly": true,
"eqeqeq": false,
"regexp": false,
"nonew": false,
"latedef": false,
"forin": false,
"globalstrict": false,
"node": true,
"immed": true,
"newcap": true,
"noarg": true,
"bitwise": true,
"sub": true,
"undef": true,
"unused": true,
"trailing": true,
"devel": true,
"jquery": true,
"nonstandard": true,
"boss": true,
"eqnull": true,
"browser": true,
"debug": true,
"globals": {
"casper": true,
"Raphael": true,
"require": true,
"console": true,
"describe": true,
"expect": true,
"it": true,
"runs": true,
"waitsFor": true,
"exports": true,
"module": true,
"prompt": true,
"process": true
}
}
================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
node_js:
- "10"
- "8"
before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH="$HOME/.yarn/bin:$PATH"
cache:
yarn: true
directories:
- "node_modules"
script:
- ./checkgit.sh "Source files were modified before build; is yarn.lock out of sync with package.json?" || travis_terminate $?
- yarn gulp
- ./checkgit.sh "Source files were modified by the build" || travis_terminate $?
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
LearnGitBranching is an interactive git visualization tool for teaching git concepts through a game-like interface. It's a 100% client-side JavaScript application that simulates git operations and renders them visually using Raphael.js for SVG graphics.
## Build & Development Commands
### Setup
```bash
yarn install
```
### Building
```bash
yarn gulp fastBuild # Fast build without tests/linting
yarn gulp build # Full production build with tests & lint
yarn gulp watching # Watch mode - rebuilds on file changes
```
### Testing & Linting
```bash
yarn test # Run Jasmine test suite
yarn test:coverage # Run tests with nyc coverage
gulp jshint # Run JSHint linter
gulp lintStrings # Validate internationalization strings
```
### Development Server
```bash
yarn dev # Start Vite dev server
# After building, open index.html directly in browser
```
### Single Test Execution
```bash
# Run specific test file
npx gulp-jasmine __tests__/git.spec.js
```
## Architecture Overview
### Core Components
**GitEngine** (`src/js/git/index.js`)
- The heart of the application - simulates all git operations
- Manages the commit graph, branches, tags, refs, and HEAD
- Processes commands and updates the visualization
- Supports both git and Mercurial (hg) modes via `mode` property
- Uses EventBaton pattern for command dispatching
**Visualization System** (`src/js/visuals/`)
- `visualization.js`: Main visualization controller
- `tree.js`: Renders the commit tree using Raphael.js
- `visNode.js`, `visBranch.js`, `visTag.js`, `visEdge.js`: Individual visual components
- `animation/`: Animation system using promise chains (Q library)
**Command Processing** (`src/js/commands/index.js`, `src/js/git/commands.js`)
- Commands are defined with regex patterns, options, and execute functions
- Supports both git and Mercurial command sets
- Command delegation system allows commands to internally invoke other commands
- All git commands are simulated - they manipulate in-memory data structures
**Levels System** (`src/levels/`)
- Each level is a JavaScript module exporting level configuration
- Organized into sequences: intro, rampup, move, mixed, advanced, remote, remoteAdvanced
- Level definition includes: name, goal description, starting tree state, solution validation
- See `src/levels/index.js` for all level sequences
### Flux Architecture
The app uses a Flux-like architecture:
**Dispatcher** (`src/js/dispatcher/AppDispatcher.js`)
- Central event bus using Facebook's Flux Dispatcher
- Handles VIEW_ACTION and URI_ACTION payload sources
**Stores** (`src/js/stores/`)
- `CommandLineStore.js`: Manages command line state
- `LevelStore.js`: Tracks current level and progress
- `LocaleStore.js`: Handles internationalization
- `GlobalStateStore.js`: Global application state
**Actions** (`src/js/actions/`)
- Action creators for CommandLine, Level, Locale, and GlobalState
### UI Components
**Backbone Views** (`src/js/views/`)
- Legacy Backbone.js views for modals, dialogs, builders
- `gitDemonstrationView.js`: Shows animated git command demonstrations
**React Components** (`src/js/react_views/`)
- Modern React components for command line, toolbar, helper bars
- Command history view with syntax highlighting
### Key Patterns
**EventBaton** (`src/js/util/eventBaton.js`)
- Pattern for transferring event handling responsibility
- Used extensively for command processing and level events
**TreeCompare** (`src/js/graph/treeCompare.js`)
- Compares two commit trees for level completion validation
- Handles branch positions, commit structure, tags
**Animation Chains**
- All visual updates go through animation queue
- Uses Q promises to chain animations
- Can be disabled for testing (headless mode)
## Project Structure
```
src/
├── js/
│ ├── app/ # Application bootstrapping
│ ├── git/ # Git engine and commands
│ ├── mercurial/ # Mercurial (hg) support
│ ├── commands/ # Command parsing and execution
│ ├── level/ # Level loading and validation
│ ├── visuals/ # Visualization and animation
│ ├── views/ # Backbone views
│ ├── react_views/ # React components
│ ├── stores/ # Flux stores
│ ├── actions/ # Flux actions
│ ├── models/ # Backbone models and collections
│ ├── intl/ # Internationalization
│ ├── graph/ # Tree comparison logic
│ └── util/ # Utilities
├── levels/ # Level definitions organized by category
├── style/ # CSS files
└── template.index.html # HTML template
__tests__/ # Jasmine test specs
```
## Build Process
The gulp build process:
1. Browserify bundles all JS files (including JSX with Babel transform)
2. CSS files concatenated and minified (production)
3. Files are hashed for cache busting
4. `template.index.html` is processed with hashed filenames to generate `index.html`
5. Tests run with Jasmine, linting with JSHint
Production builds minify JS with Terser and HTML with html-minifier.
## Key Technologies
- **Backbone.js**: MVC framework (legacy parts)
- **React 17**: UI components (modern parts)
- **Flux**: Unidirectional data flow
- **Raphael.js**: SVG graphics for visualization
- **Q**: Promises for animation chains
- **jQuery/jQuery UI**: DOM manipulation and dialogs
- **Browserify + Babel**: Module bundling and JSX transform
- **Gulp**: Build automation
- **Jasmine**: Testing framework
## Testing Conventions
- Tests in `__tests__/` directory
- Headless git engine for testing (`src/js/git/headless.js`)
- Use `create.js` helper to set up test git trees
- Mock commands with `mock.js` utility
- Tests cover git operations, remote operations, levels, tree comparison
## Remote Repository Simulation
Remote operations (push/pull/fetch) are simulated by creating a separate GitEngine instance for the "origin" repository. The `o/` prefix denotes remote tracking branches.
## Internationalization
String translations in `src/js/intl/strings.js`. Use `intl.str()` and `intl.getDialog()` to access localized strings. Locale validation via `checkStrings.js`.
================================================
FILE: CNAME
================================================
learnGitBranching.js.org
================================================
FILE: CONTRIBUTING.md
================================================
## Welcome to the Learn Git Branching contributing guide!
We have a pretty relaxed environment in this project so there's no formal template to submit Pull Requests for. Contributions generally fall into two buckets:
### Translations
I welcome all translation improvements or additions! The levels are stored in giant JSON blobs, keyed by locale for each string. This means its somewhat awkward to add new translations, since you have to edit the JSON manually.
### Bug Fixes, New Features, etc
These are great too! If you are adding new functionality to the git engine, I would try to add some tests in the `__tests__` folder. It's pretty simple, you can just input a bunch of git commands and show the expected tree state afterwards.
For bug fixes or CSS/style layout issues, simply attach screenshots of the before and after. For more obscure browsers, targeted CSS rules by browser is a bit more preferred.
Thanks for stopping by!
================================================
FILE: Dockerfile
================================================
FROM node:14.20.0-alpine3.16 as build
RUN apk add git --no-cache
WORKDIR "/src"
COPY . /src
RUN yarn install && \
yarn cache clean
RUN yarn gulp build
FROM scratch AS export
WORKDIR /
COPY --from=build /src/index.html .
COPY --from=build /src/build ./build
FROM nginx:stable-alpine
WORKDIR /usr/share/nginx/html/
COPY . .
# Override the local source with the built artifacts
COPY --from=export . .
================================================
FILE: LICENSE.md
================================================
## MIT License
Copyright (c) 2012-2025 Peter Cottle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
ifeq ($(OS),Windows_NT)
SHELL := pwsh.exe
else
SHELL := pwsh
endif
.SHELLFLAGS := -NoProfile -Command
REGISTRY_NAME := ghcr.io/
REPOSITORY_NAME := pcottle/
IMAGE_NAME := learngitbranching
TAG := :latest
.PHONY: all clean test
all: build
getcommitid:
$(eval COMMITID = $(shell git log -1 --pretty=format:'%H'))
getbranchname:
$(eval BRANCH_NAME = $(shell (git branch --show-current ) -replace '/','.'))
build:
docker run --rm -v $${PWD}:/mnt --workdir /mnt node:14.20.0-alpine3.16 yarn install
docker run --rm -v $${PWD}:/mnt --workdir /mnt node:14.20.0-alpine3.16 yarn gulp fastBuild
build_docker: getcommitid getbranchname
docker build -t $(REGISTRY_NAME)$(REPOSITORY_NAME)$(IMAGE_NAME)$(TAG) -t $(REGISTRY_NAME)$(REPOSITORY_NAME)$(IMAGE_NAME):$(BRANCH_NAME) -t $(REGISTRY_NAME)$(REPOSITORY_NAME)$(IMAGE_NAME):$(BRANCH_NAME)_$(COMMITID) .
docker build --target export --output type=local,dest=learnGitBranching_$(BRANCH_NAME)_$(COMMITID) .
run:
docker run -p 8080:80 $(REGISTRY_NAME)$(REPOSITORY_NAME)$(IMAGE_NAME)$(TAG)
clean:
echo 'not implemented'
test:
echo 'not implemented'
================================================
FILE: README.md
================================================
# LearnGitBranching
[](https://github.com/pcottle/learnGitBranching/pulls)
LearnGitBranching is a git repository visualizer, sandbox, and a series of educational tutorials and challenges. Its primary purpose is to help developers understand git through the power of visualization (something that's absent when working on the command line). This is achieved through a game with different levels to get acquainted with the different git commands.
You can input a variety of commands into LearnGitBranching (LGB) -- as commands are processed, the nearby commit tree will dynamically update to reflect the effects of each command:

This visualization combined with tutorials and "levels" can help both beginners and intermediate developers polish their version control skills. A quick demo is available here:
https://pcottle.github.io/learnGitBranching/?demo
Or, you can launch the application normally here:
https://pcottle.github.io/learnGitBranching/
### Sandbox Mode
By default the application launches in "sandbox mode" with a basic repository already created. Here you can enter commands and mess around with a repository as much as you like. Keep in mind you can
* `undo` to undo the effects of the last command
* `reset` to start over with a clean slate (works in levels too)
* `git clone` to simulate remote repositories!
Sandbox mode can be great for demonstrating something to a friend, but the real learning is with levels...
## Levels
Type `levels` to see the available lessons / challenges (and which ones you have solved so far). Each level series aims to teach some high-level git concept, and each tab of levels separates major worlds of info (like remote repositories versus local).
For some added fun, there is a "git golf" concept where we keep track of how many commands you use to solve each level. See if you can match all of our records!
### Sharing permalinks
You can share a link to LearnGitBranching with an arbitrary set of commands that will execute upon load by using the `command` URL parameter. You will also likely want to disable the intro dialog for this case with the `NODEMO` url param; here is [an example](https://learngitbranching.js.org/?NODEMO&command=echo%20%22hello%22;%20git%20commit) to get started.
### Level Builder
You can build levels with the `build level` command. A dialog will walk you through the process, and at the end it will show you a JSON blob that represents the level you just created. Paste that in a [gist](https://gist.github.com) or directly into an issue and I can check it out / merge in your changes! You can also share this level directly with friends by having them run `import level` and paste the JSON in the resulting text field, or simply send them a custom URL with the the gist ID in the parameters, like so:
https://pcottle.github.io/learnGitBranching/?gist_level_id=a84407351f9c9f0cb241
## Reporting Bugs / Opening Issues
When reporting bugs, try running the command `debug_copyTree()` in your JS console when in a state just before reproducing a bug. This can avoid having to copy all the commands you used to get into a specific state. (I can then use the `importTreeNow` command to get to that exact state)
## Building yourself / Contributing Functionality
For contributing core functionality in the app, you'll probably want to test your changes
at least once before submitting a pull request. That means you'll need the "gulp.js" build tool to build the app:
https://gulpjs.com/docs/en/getting-started/quick-start
You'll also need `yarn` to download all the dependencies of the project.
The general workflow / steps are below:
```bash
git clone <your fork of the repo>
cd learnGitBranching
yarn install
git checkout -b newAwesomeFeature
vim ./src/js/git/index.js # some changes
yarn gulp fastBuild # skips tests and linting, faster build
# after building you can open up your browser to the index.html
open ./index.html
# file generated and see your changes
vim ./src/js/git/index.js # more changes
yarn gulp build # runs tests and lint
git commit -am "My new sweet feature!"
git push
# go online and request a pull
```
Alternatively, you can also build and run the app in a pre-configured online workspace:
[](https://gitpod.io/#https://github.com/pcottle/learnGitBranching/blob/main/src/js/git/index.js)
[](https://app.codeanywhere.com/#https://github.com/pcottle/learnGitBranching)
## Other Technical Details
LearnGitBranching is a pretty simple application (from a technical perspective). There's no backend database or any AJAX requests -- it's a 100% clientside application written in JavaScript. The production version (on github.io) literally just serves up an HTML page with some JS and CSS.
Here is the high-level process of the build:
* CSS is written into just one stylesheet (there is not a whole ton of styling)
* New HTML is written into a template HTML file (`template.index.html`). This is only needed
for new views
* The app is "built", which outputs:
* `index.html` in the root directory
* CSS and JS files in `./build` directory
* If the app is being built for production, then these CSS and JS files
are hashed (to bust caches) and tests are run
* That's it!
Thus, if you build the app locally, all you have to do in order to run the app is just open up `index.html` in the root directory of the repo. Pretty simple
### Docker
You can run the most recently built stable image with `docker run -p 8080:80 ghcr.io/pcottle/learngitbranching:main`. Access your environment with at [http://localhost:8080/](<http://localhost:8080/>)
You can build the app and image with the command: `docker build -t ghcr.io/pcottle/learngitbranching:latest`. See the [Makefile](Makefile) for information on how to build locally with docker.
## Some of our amazing contributors
[//]: contributor-faces
<a href="https://github.com/pcottle"><img src="https://avatars0.githubusercontent.com/u/1135007?v=4" title="pcottle" width="80" height="80"></a>
<a href="https://github.com/Hongarc"><img src="https://avatars1.githubusercontent.com/u/19208123?v=4" title="Hongarc" width="80" height="80"></a>
<a href="https://github.com/twmht"><img src="https://avatars1.githubusercontent.com/u/1567200?v=4" title="twmht" width="80" height="80"></a>
<a href="https://github.com/JuhoKang"><img src="https://avatars1.githubusercontent.com/u/4745294?v=4" title="JuhoKang" width="80" height="80"></a>
<a href="https://github.com/sunshowers"><img src="https://avatars3.githubusercontent.com/u/180618?v=4" title="sunshowers" width="80" height="80"></a>
<a href="https://github.com/sPATROL"><img src="https://avatars0.githubusercontent.com/u/11875983?v=4" title="sPATROL" width="80" height="80"></a>
<a href="https://github.com/coyl"><img src="https://avatars1.githubusercontent.com/u/274452?v=4" title="coyl" width="80" height="80"></a>
<a href="https://github.com/alexeiberkov"><img src="https://avatars1.githubusercontent.com/u/4151345?v=4" title="alexeiberkov" width="80" height="80"></a>
<a href="https://github.com/aschrab"><img src="https://avatars1.githubusercontent.com/u/39620?v=4" title="aschrab" width="80" height="80"></a>
<a href="https://github.com/bcbcarl"><img src="https://avatars0.githubusercontent.com/u/135734?v=4" title="bcbcarl" width="80" height="80"></a>
<a href="https://github.com/ahonorat"><img src="https://avatars1.githubusercontent.com/u/5851945?v=4" title="ahonorat" width="80" height="80"></a>
<a href="https://github.com/nem75"><img src="https://avatars0.githubusercontent.com/u/1327785?v=4" title="nem75" width="80" height="80"></a>
<a href="https://github.com/eatdrinksleepcode"><img src="https://avatars0.githubusercontent.com/u/2099560?v=4" title="eatdrinksleepcode" width="80" height="80"></a>
<a href="https://github.com/nlehuby"><img src="https://avatars3.githubusercontent.com/u/919962?v=4" title="nlehuby" width="80" height="80"></a>
<a href="https://github.com/alexandear"><img src="https://avatars2.githubusercontent.com/u/3228886?v=4" title="alexandear" width="80" height="80"></a>
<a href="https://github.com/PanAeon"><img src="https://avatars3.githubusercontent.com/u/686076?v=4" title="PanAeon" width="80" height="80"></a>
<a href="https://github.com/donkirkby"><img src="https://avatars1.githubusercontent.com/u/1639148?v=4" title="donkirkby" width="80" height="80"></a>
<a href="https://github.com/lroellin"><img src="https://avatars1.githubusercontent.com/u/3150983?v=4" title="lroellin" width="80" height="80"></a>
<a href="https://github.com/ptsoccer"><img src="https://avatars1.githubusercontent.com/u/1102725?v=4" title="ptsoccer" width="80" height="80"></a>
<a href="https://github.com/jankeromnes"><img src="https://avatars2.githubusercontent.com/u/599268?v=4" title="jankeromnes" width="80" height="80"></a>
<a href="https://github.com/qiansen1386"><img src="https://avatars2.githubusercontent.com/u/1759658?v=4" title="qiansen1386" width="80" height="80"></a>
<a href="https://github.com/renderf0x"><img src="https://avatars1.githubusercontent.com/u/6155643?v=4" title="renderf0x" width="80" height="80"></a>
<a href="https://github.com/filipefilardi"><img src="https://avatars1.githubusercontent.com/u/7308241?v=4" title="filipefilardi" width="80" height="80"></a>
<a href="https://github.com/rebangm"><img src="https://avatars2.githubusercontent.com/u/1638136?v=4" title="rebangm" width="80" height="80"></a>
<a href="https://github.com/marcolivierarsenault"><img src="https://avatars2.githubusercontent.com/u/2634090?v=4" title="marcolivierarsenault" width="80" height="80"></a>
<a href="https://github.com/pengi"><img src="https://avatars0.githubusercontent.com/u/1087673?v=4" title="pengi" width="80" height="80"></a>
<a href="https://github.com/waldyrious"><img src="https://avatars2.githubusercontent.com/u/478237?v=4" title="waldyrious" width="80" height="80"></a>
<a href="https://github.com/tym-network"><img src="https://avatars1.githubusercontent.com/u/2879545?v=4" title="tym-network" width="80" height="80"></a>
<a href="https://github.com/zhyu"><img src="https://avatars1.githubusercontent.com/u/1728523?v=4" title="zhyu" width="80" height="80"></a>
<a href="https://github.com/mgarciaisaia"><img src="https://avatars1.githubusercontent.com/u/1190974?v=4" title="mgarciaisaia" width="80" height="80"></a>
<a href="https://github.com/YourSenseiCreeper"><img src="https://avatars1.githubusercontent.com/u/6324814?&v=4" title="YourSenseiCreeper" width="80" height="80"></a>
<a href="https://github.com/olsza"><img src="https://avatars1.githubusercontent.com/u/12556170?v=4" title="Olsza" width="80" height="80"></a>
[//]: contributor-faces
## Helpful Folks
A big shoutout to these brave souls for extensively testing our sandbox and finding bugs and/or inconsistencies:
* Nikita Kouevda
* Maksim Ioffe
* Dan Miller
And the following heroes for assisting in translating:
* Jake Chen
* 우리깃 ("urigit")
* "bcho"
* "scientific-coder"
* "ace-coder"
* Joël Thieffry
* Jens Bremmekamp ("nem75")
* "hilojack"
* Ming-Hsuan-Tu ("twmht")
* Mikhail Usov ("mikhailusov")
* Matias Garcia Isaia ("mgarciaisaia")
* Marc-Olivier Arsenault ("marcolivierarsenault")
* Eroany H Leader ("lhyqy5")
* Honorat ("ahonorat")
* Vasil Kulakov ("coyl") & Lyubov Agadjanyan ("shayenblue")
* Aliaksei Berkau ("alexeiberkov")
* Mizunashi Mana ("mizunashi-mana")
* YourSenseiCreeper
* Olsza
Also huge shoutout for everyone who has put up a pull request that was pulled! Check out the 30+ contributors we have in the [Contributors View](https://github.com/pcottle/learnGitBranching/graphs/contributors)
And everyone who has reported an issue that was successfully closed!
================================================
FILE: __tests__/CommandLineStore.spec.js
================================================
var CommandLineActions = require('../src/js/actions/CommandLineActions');
var CommandLineStore = require('../src/js/stores/CommandLineStore');
describe('this store', function() {
it('starts with no entries', function() {
expect(CommandLineStore.getCommandHistoryLength())
.toEqual(0);
});
it('receives new commands', function() {
var command = 'git commit; git checkout HEAD';
CommandLineActions.submitCommand(command);
expect(CommandLineStore.getCommandHistoryLength())
.toEqual(1);
expect(CommandLineStore.getCommandHistory()[0])
.toEqual(command);
var newCommand = 'echo "yo dude";';
CommandLineActions.submitCommand(newCommand);
expect(CommandLineStore.getCommandHistoryLength())
.toEqual(2);
expect(CommandLineStore.getCommandHistory()[0])
.toEqual(newCommand);
expect(CommandLineStore.getCommandHistory()[1])
.toEqual(command);
});
it('slices after max length', function() {
var maxLength = CommandLineStore.getMaxHistoryLength();
var numOver = 10;
for (var i = 0; i < maxLength + numOver; i++) {
CommandLineActions.submitCommand('commandNum' + i);
}
var numNow = 11 + numOver;
expect(
CommandLineStore.getCommandHistoryLength()
).toEqual(numNow);
expect(
CommandLineStore.getCommandHistory()[0]
).toEqual('commandNum109');
expect(
CommandLineStore.getCommandHistory()[numNow - 1]
).toEqual('commandNum89');
});
});
================================================
FILE: __tests__/GlobalStateStore.spec.js
================================================
var GlobalStateActions = require('../src/js/actions/GlobalStateActions');
var GlobalStateStore = require('../src/js/stores/GlobalStateStore');
describe('this store', function() {
it('is can change animating', function() {
expect(GlobalStateStore.getIsAnimating()).toEqual(false);
GlobalStateActions.changeIsAnimating(true);
expect(GlobalStateStore.getIsAnimating()).toEqual(true);
GlobalStateActions.changeIsAnimating(false);
expect(GlobalStateStore.getIsAnimating()).toEqual(false);
});
it('can change flip treey', function() {
expect(GlobalStateStore.getFlipTreeY()).toEqual(false);
GlobalStateActions.changeFlipTreeY(true);
expect(GlobalStateStore.getFlipTreeY()).toEqual(true);
GlobalStateActions.changeFlipTreeY(false);
expect(GlobalStateStore.getFlipTreeY()).toEqual(false);
});
});
================================================
FILE: __tests__/LevelStore.spec.js
================================================
var LevelActions = require('../src/js/actions/LevelActions');
var LevelStore = require('../src/js/stores/LevelStore');
describe('this store', function() {
it('has sequences and levels', function() {
var sequenceMap = LevelStore.getSequenceToLevels();
Object.keys(sequenceMap).forEach(function(levelSequence) {
expect(LevelStore.getSequences().indexOf(levelSequence) >= 0)
.toEqual(true);
sequenceMap[levelSequence].forEach(function(level) {
expect(LevelStore.getLevel(level.id)).toEqual(level);
}.bind(this));
}.bind(this));
});
it('can solve a level and then reset', function() {
var sequenceMap = LevelStore.getSequenceToLevels();
var firstLevel = sequenceMap[
Object.keys(sequenceMap)[0]
][0];
expect(LevelStore.isLevelSolved(firstLevel.id))
.toEqual(false);
LevelActions.setLevelSolved(firstLevel.id, false);
expect(LevelStore.isLevelSolved(firstLevel.id))
.toEqual(true);
LevelActions.resetLevelsSolved();
expect(LevelStore.isLevelSolved(firstLevel.id))
.toEqual(false);
});
it('can solve a level with best status and then reset', function() {
var sequenceMap = LevelStore.getSequenceToLevels();
var firstLevel = sequenceMap[
Object.keys(sequenceMap)[0]
][0];
expect(LevelStore.isLevelBest(firstLevel.id))
.toEqual(false);
LevelActions.setLevelSolved(firstLevel.id, true);
expect(LevelStore.isLevelBest(firstLevel.id))
.toEqual(true);
LevelActions.resetLevelsSolved();
expect(LevelStore.isLevelBest(firstLevel.id))
.toEqual(false);
});
});
================================================
FILE: __tests__/LocaleStore.spec.js
================================================
var LocaleActions = require('../src/js/actions/LocaleActions');
var LocaleStore = require('../src/js/stores/LocaleStore');
describe('LocaleStore', function() {
it('has default locale', function() {
expect(LocaleStore.getLocale())
.toEqual(LocaleStore.getDefaultLocale());
});
it('changes locales', function() {
expect(LocaleStore.getLocale()).toEqual('en_US');
LocaleActions.changeLocale('ja_JP');
expect(LocaleStore.getLocale()).toEqual('ja_JP');
});
it('changes locales from headers', function() {
var headerLocaleMap = LocaleStore.getHeaderLocaleMap();
Object.keys(headerLocaleMap).forEach(function(header) {
LocaleActions.changeLocaleFromHeader(header);
expect(LocaleStore.getLocale()).toEqual(
headerLocaleMap[header]
);
});
});
it('changes locales from languages', function() {
var langLocaleMap = LocaleStore.getLangLocaleMap();
Object.keys(langLocaleMap).forEach(function(lang) {
LocaleActions.changeLocaleFromHeader(lang);
expect(LocaleStore.getLocale()).toEqual(
langLocaleMap[lang]
);
});
});
});
================================================
FILE: __tests__/animation.spec.js
================================================
var AnimationModule = require('../src/js/visuals/animation/index');
var PromiseAnimation = AnimationModule.PromiseAnimation;
var Animation = AnimationModule.Animation;
var createDeferred = require('../src/js/util/promise').createDeferred;
describe('Promise animation', function() {
it('Will execute the closure', function() {
var value = 0;
var closure = function() {
value++;
};
var animation = new PromiseAnimation({
deferred: createDeferred(),
closure: closure
});
animation.play();
expect(value).toBe(1);
});
it('also takes animation packs', function() {
var value = 0;
var animation = new PromiseAnimation({
animation: function() { value++; }
});
animation.play();
expect(value).toBe(1);
});
});
================================================
FILE: __tests__/base.js
================================================
var HeadlessGit = require('../src/js/git/headless').HeadlessGit;
var TreeCompare = require('../src/js/graph/treeCompare.js');
var loadTree = function(json) {
return JSON.parse(unescape(json));
};
var compareLevelTree = function(headless, levelBlob) {
var actualTree = headless.gitEngine.printTree();
return TreeCompare.dispatchFromLevel(levelBlob, actualTree);
};
var compareAnswer = function(headless, expectedJSON) {
var expectedTree = loadTree(expectedJSON);
var actualTree = headless.gitEngine.exportTree();
return TreeCompare.compareTrees(expectedTree, actualTree);
};
var getHeadlessSummary = function(headless) {
var tree = headless.gitEngine.exportTree();
TreeCompare.reduceTreeFields([tree]);
return tree;
};
var expectLevelAsync = function(headless, levelBlob) {
var command = levelBlob.solutionCommand;
if (command.indexOf('git rebase -i') !== -1) {
// don't do interactive rebase levels
return;
}
return headless.sendCommand(command).then(function() {
expect(compareLevelTree(headless, levelBlob)).toBeTruthy(
'Level "' + levelBlob['name']['en_US'] + '" should get solved'
);
});
};
var expectTreeAsync = function(command, expectedJSON, startJSON) {
var headless = new HeadlessGit();
if (startJSON) {
headless.gitEngine.loadTreeFromString(startJSON);
}
return headless.sendCommand(command).then(function() {
expect(compareAnswer(headless, expectedJSON)).toBeTruthy();
});
};
var expectLevelSolved = function(levelBlob) {
var headless = new HeadlessGit();
if (levelBlob.startTree) {
headless.gitEngine.loadTreeFromString(levelBlob.startTree);
}
expectLevelAsync(headless, levelBlob);
};
var runCommand = function(command, resultHandler) {
var headless = new HeadlessGit();
var msg = null;
return new Promise(function(resolve) {
headless.sendCommand(command, { resolve: resolve });
}).then(function(commands) {
if (commands && commands.length) {
msg = commands[commands.length - 1].get('error').get('msg');
}
resultHandler(msg);
});
};
var TIME = 150;
// useful for throwing garbage and then expecting one commit
var ONE_COMMIT_TREE = '{"branches":{"main":{"target":"C2","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}';
module.exports = {
loadTree: loadTree,
compareAnswer: compareAnswer,
TIME: TIME,
expectTreeAsync: expectTreeAsync,
expectLevelSolved: expectLevelSolved,
ONE_COMMIT_TREE: ONE_COMMIT_TREE,
runCommand: runCommand
};
================================================
FILE: __tests__/collections.spec.js
================================================
var collections = require('../src/js/models/collections');
var Command = require('../src/js/models/commandModel').Command;
describe('Collections', function() {
describe('CommandCollection', function() {
it('should create an empty collection', function() {
var col = new collections.CommandCollection();
expect(col.length).toBe(0);
});
it('should add items', function() {
var col = new collections.CommandCollection();
// Use 'git commit' which is a supported command
var cmd = new Command({rawStr: 'git commit'});
col.add(cmd);
expect(col.length).toBe(1);
});
it('should retrieve items by index with at()', function() {
var col = new collections.CommandCollection();
var cmd = new Command({rawStr: 'git commit'});
col.add(cmd);
expect(col.at(0)).toBe(cmd);
});
it('should support toArray()', function() {
var col = new collections.CommandCollection();
var cmd1 = new Command({rawStr: 'git commit'});
var cmd2 = new Command({rawStr: 'git checkout main'});
col.add(cmd1);
col.add(cmd2);
var arr = col.toArray();
expect(arr.length).toBe(2);
expect(arr[0]).toBe(cmd1);
expect(arr[1]).toBe(cmd2);
});
});
describe('CommitCollection', function() {
it('should create an empty collection', function() {
var col = new collections.CommitCollection();
expect(col.length).toBe(0);
});
});
describe('BranchCollection', function() {
it('should create an empty collection', function() {
var col = new collections.BranchCollection();
expect(col.length).toBe(0);
});
});
describe('TagCollection', function() {
it('should create an empty collection', function() {
var col = new collections.TagCollection();
expect(col.length).toBe(0);
});
});
describe('CommandBuffer', function() {
it('should be created with a collection', function() {
var col = new collections.CommandCollection();
var buffer = new collections.CommandBuffer({collection: col});
expect(buffer).toBeTruthy();
});
it('should have an empty buffer initially', function() {
var col = new collections.CommandCollection();
var buffer = new collections.CommandBuffer({collection: col});
expect(buffer.buffer.length).toBe(0);
});
it('should add commands to buffer when collection receives add', function() {
var col = new collections.CommandCollection();
var buffer = new collections.CommandBuffer({collection: col});
var cmd = new Command({rawStr: 'git commit'});
col.add(cmd);
expect(buffer.buffer.length).toBe(1);
buffer.clear();
});
it('should clear timeout on clear()', function() {
var col = new collections.CommandCollection();
var buffer = new collections.CommandBuffer({collection: col});
buffer.setTimeout();
expect(buffer.timeout).toBeTruthy();
buffer.clear();
expect(buffer.timeout).toBe(null);
});
});
});
================================================
FILE: __tests__/commandModel.spec.js
================================================
var Command = require('../src/js/models/commandModel').Command;
describe('Command Model', function() {
describe('initialization', function() {
it('should require rawStr', function() {
expect(function() {
new Command({});
}).toThrow();
});
it('should set createTime on init', function() {
var cmd = new Command({rawStr: 'git status'});
expect(cmd.get('createTime')).toBeTruthy();
});
it('should initialize generalArgs as empty array', function() {
var cmd = new Command({rawStr: 'git status'});
expect(Array.isArray(cmd.get('generalArgs'))).toBe(true);
});
it('should initialize warnings as empty array', function() {
var cmd = new Command({rawStr: 'git status'});
expect(Array.isArray(cmd.get('warnings'))).toBe(true);
});
it('should initialize supportedMap as empty object', function() {
var cmd = new Command({rawStr: 'git status'});
expect(typeof cmd.get('supportedMap')).toBe('object');
});
});
describe('get and set', function() {
it('should get and set values', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.set('status', 'processing');
expect(cmd.get('status')).toBe('processing');
});
it('should handle default status', function() {
var cmd = new Command({rawStr: 'git status'});
expect(cmd.get('status')).toBe('inqueue');
});
});
describe('replaceDotWithHead', function() {
it('should replace dots with HEAD', function() {
var cmd = new Command({rawStr: 'git status'});
expect(cmd.replaceDotWithHead('.')).toBe('HEAD');
expect(cmd.replaceDotWithHead('.~1')).toBe('HEAD~1');
expect(cmd.replaceDotWithHead('...')).toBe('HEADHEADHEAD');
});
});
describe('generalArgs methods', function() {
it('should get and set generalArgs', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setGeneralArgs(['arg1', 'arg2']);
expect(cmd.getGeneralArgs()).toEqual(['arg1', 'arg2']);
});
});
describe('optionsMap methods', function() {
it('should get and set optionsMap', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setOptionsMap({'-b': ['value']});
expect(cmd.getOptionsMap()).toEqual({'-b': ['value']});
});
});
describe('deleteOptions', function() {
it('should delete specified options', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setOptionsMap({'-a': ['1'], '-b': ['2'], '-c': ['3']});
cmd.deleteOptions(['-a', '-c']);
expect(cmd.getOptionsMap()).toEqual({'-b': ['2']});
});
});
describe('mapDotToHead', function() {
it('should replace dots in generalArgs and options', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setGeneralArgs(['.', '.~1']);
cmd.setOptionsMap({'-b': ['.']});
cmd.mapDotToHead();
expect(cmd.getGeneralArgs()).toEqual(['HEAD', 'HEAD~1']);
expect(cmd.getOptionsMap()).toEqual({'-b': ['HEAD']});
});
});
describe('appendOptionR', function() {
it('should append -r options to generalArgs', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setGeneralArgs(['existing']);
cmd.setOptionsMap({'-r': ['rev1', 'rev2']});
cmd.appendOptionR();
expect(cmd.getGeneralArgs()).toEqual(['existing', 'rev1', 'rev2']);
});
});
describe('prependOptionR', function() {
it('should prepend -r options to generalArgs', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setGeneralArgs(['existing']);
cmd.setOptionsMap({'-r': ['rev1', 'rev2']});
cmd.prependOptionR();
expect(cmd.getGeneralArgs()).toEqual(['rev1', 'rev2', 'existing']);
});
});
describe('impliedHead', function() {
it('should add HEAD when args.length equals min', function() {
var cmd = new Command({rawStr: 'git status'});
var args = [];
cmd.impliedHead(args, 0);
expect(args).toEqual(['HEAD']);
});
it('should not add HEAD when args.length > min', function() {
var cmd = new Command({rawStr: 'git status'});
var args = ['branch1'];
cmd.impliedHead(args, 0);
expect(args).toEqual(['branch1']);
});
});
describe('setResult', function() {
it('should set the result', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.setResult('test result');
expect(cmd.get('result')).toBe('test result');
});
});
describe('addWarning', function() {
it('should add warning to warnings array', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.addWarning('warning 1');
cmd.addWarning('warning 2');
expect(cmd.get('warnings')).toEqual(['warning 1', 'warning 2']);
});
it('should increment numWarnings', function() {
var cmd = new Command({rawStr: 'git status'});
cmd.addWarning('warning 1');
expect(cmd.get('numWarnings')).toBe(1);
cmd.addWarning('warning 2');
expect(cmd.get('numWarnings')).toBe(2);
});
});
describe('finishWith', function() {
it('should set status to finished and resolve deferred', function(done) {
var cmd = new Command({rawStr: 'git status'});
var resolved = false;
var deferred = {
resolve: function() { resolved = true; }
};
cmd.finishWith(deferred);
expect(cmd.get('status')).toBe('finished');
expect(resolved).toBe(true);
done();
});
});
describe('error handling', function() {
it('should set error status for unsupported commands', function() {
var cmd = new Command({rawStr: 'git unsupportedcommand'});
expect(cmd.get('status')).toBe('error');
expect(cmd.get('error')).toBeTruthy();
});
});
});
================================================
FILE: __tests__/create.js
================================================
var TreeCompare = require('../src/js/graph/treeCompare');
var HeadlessGit = require('../src/js/git/headless').HeadlessGit;
var fs = require('fs');
prompt = require('prompt');
function getFile(truthy) {
var filename = (truthy) ?
'./git.spec.js' :
'./remote.spec.js';
return fs.readFileSync(filename, 'utf8');
}
function writeFile(truthy, content) {
var filename = (truthy) ?
'./git.spec.js' :
'./remote.spec.js';
fs.writeFileSync(filename, content);
}
prompt.start();
prompt.get(
['command', 'whatItDoes', 'intoGitSpec'],
function(err, result) {
var headless = new HeadlessGit();
headless.sendCommand(result.command);
setTimeout(function() {
var testCase = '\t\texpectTreeAsync(\n' +
"\t\t\t'" + result.command + "',\n" +
"\t\t\t'" + headless.gitEngine.printTree() + "'\n" +
"\t\t);\n";
console.log(testCase);
// now add it
var testFile = getFile(result.intoGitSpec);
// insert after the last })
var toSlice = testFile.lastIndexOf('})');
var partOne = testFile.slice(0, toSlice);
var partTwo = testFile.slice(toSlice);
var funcCall = "\tit('" + result.whatItDoes + "', function() {\n" +
testCase + "\t});\n\n";
writeFile(result.intoGitSpec, partOne + funcCall + partTwo);
}, 100);
}
);
================================================
FILE: __tests__/errors.spec.js
================================================
var errors = require('../src/js/util/errors');
describe('Error Models', function() {
describe('GitError', function() {
it('should store and retrieve msg', function() {
var err = new errors.GitError({msg: 'test message'});
expect(err.getMsg()).toBe('test message');
});
it('should have correct type', function() {
var err = new errors.GitError({msg: 'test'});
expect(err.get('type')).toBe('Git Error');
});
it('should convert to string', function() {
var err = new errors.GitError({msg: 'oops'});
expect(err.toString()).toBe('Git Error: oops');
});
it('should be instanceof GitError', function() {
var err = new errors.GitError({msg: 'test'});
expect(err instanceof errors.GitError).toBe(true);
});
});
describe('Warning', function() {
it('should store and retrieve msg', function() {
var err = new errors.Warning({msg: 'warning message'});
expect(err.getMsg()).toBe('warning message');
});
it('should have correct type', function() {
var err = new errors.Warning({msg: 'test'});
expect(err.get('type')).toBe('Warning');
});
});
describe('CommandProcessError', function() {
it('should store and retrieve msg', function() {
var err = new errors.CommandProcessError({msg: 'process error'});
expect(err.getMsg()).toBe('process error');
});
it('should have correct type', function() {
var err = new errors.CommandProcessError({msg: 'test'});
expect(err.get('type')).toBe('Command Process Error');
});
});
describe('CommandResult', function() {
it('should store and retrieve msg', function() {
var err = new errors.CommandResult({msg: 'result'});
expect(err.getMsg()).toBe('result');
});
it('should have correct type', function() {
var err = new errors.CommandResult({msg: 'test'});
expect(err.get('type')).toBe('Command Result');
});
});
describe('filterError', function() {
it('should not throw for GitError', function() {
var err = new errors.GitError({msg: 'test'});
expect(function() {
errors.filterError(err);
}).not.toThrow();
});
it('should not throw for Warning', function() {
var err = new errors.Warning({msg: 'test'});
expect(function() {
errors.filterError(err);
}).not.toThrow();
});
it('should not throw for CommandProcessError', function() {
var err = new errors.CommandProcessError({msg: 'test'});
expect(function() {
errors.filterError(err);
}).not.toThrow();
});
it('should not throw for CommandResult', function() {
var err = new errors.CommandResult({msg: 'test'});
expect(function() {
errors.filterError(err);
}).not.toThrow();
});
it('should throw for unknown errors', function() {
var err = new Error('unknown');
expect(function() {
errors.filterError(err);
}).toThrow();
});
});
});
================================================
FILE: __tests__/eventEmitter.spec.js
================================================
var eventEmitter = require('../src/js/util/eventEmitter');
describe('EventEmitter', function() {
describe('on() and trigger()', function() {
it('should call callback when event is triggered', function() {
var emitter = new eventEmitter.EventEmitter();
var called = false;
emitter.on('test', function() {
called = true;
});
emitter.trigger('test');
expect(called).toBe(true);
});
it('should pass arguments to callback', function() {
var emitter = new eventEmitter.EventEmitter();
var receivedArgs = null;
emitter.on('test', function(arg1, arg2) {
receivedArgs = [arg1, arg2];
});
emitter.trigger('test', 'hello', 'world');
expect(receivedArgs).toEqual(['hello', 'world']);
});
it('should use EventEmitter as default context when no context provided', function() {
var emitter = new eventEmitter.EventEmitter();
var receivedContext = null;
emitter.on('test', function() {
receivedContext = this;
});
emitter.trigger('test');
expect(receivedContext).toBe(emitter);
});
it('should use provided context when context is passed', function() {
var emitter = new eventEmitter.EventEmitter();
var customContext = { name: 'custom' };
var receivedContext = null;
emitter.on('test', function() {
receivedContext = this;
}, customContext);
emitter.trigger('test');
expect(receivedContext).toBe(customContext);
});
it('should allow callback to access properties on custom context', function() {
var emitter = new eventEmitter.EventEmitter();
var obj = {
value: 42,
handler: function() {
return this.value;
}
};
var result = null;
emitter.on('test', function() {
result = this.value;
}, obj);
emitter.trigger('test');
expect(result).toBe(42);
});
});
describe('off()', function() {
it('should remove all listeners for an event when no callback specified', function() {
var emitter = new eventEmitter.EventEmitter();
var count = 0;
emitter.on('test', function() { count++; });
emitter.on('test', function() { count++; });
emitter.off('test');
emitter.trigger('test');
expect(count).toBe(0);
});
it('should remove specific callback', function() {
var emitter = new eventEmitter.EventEmitter();
var count1 = 0;
var count2 = 0;
var callback1 = function() { count1++; };
var callback2 = function() { count2++; };
emitter.on('test', callback1);
emitter.on('test', callback2);
emitter.off('test', callback1);
emitter.trigger('test');
expect(count1).toBe(0);
expect(count2).toBe(1);
});
});
describe('once()', function() {
it('should only fire callback once', function() {
var emitter = new eventEmitter.EventEmitter();
var count = 0;
emitter.once('test', function() {
count++;
});
emitter.trigger('test');
emitter.trigger('test');
emitter.trigger('test');
expect(count).toBe(1);
});
});
describe('createEvents() with bound methods', function() {
it('should demonstrate context issue when on() is bound to emitter', function() {
// This test demonstrates the bug that was fixed
// When createEvents() is used and methods are bound to the emitter,
// the default context becomes the emitter, not the calling object
var events = eventEmitter.createEvents();
var myObject = {
value: 'myObject',
_events: events._events,
on: events.on.bind(events),
trigger: events.trigger.bind(events)
};
var receivedContext = null;
// Without explicit context, 'this' inside callback will be the EventEmitter
myObject.on('test', function() {
receivedContext = this;
});
myObject.trigger('test');
// The context is the EventEmitter, NOT myObject
expect(receivedContext).toBe(events);
expect(receivedContext).not.toBe(myObject);
});
it('should use correct context when explicitly passed', function() {
// This is the FIX - always pass 'this' as the third argument
var events = eventEmitter.createEvents();
var myObject = {
value: 'myObject',
_events: events._events,
on: events.on.bind(events),
trigger: events.trigger.bind(events)
};
var receivedContext = null;
// With explicit context, 'this' inside callback will be myObject
myObject.on('test', function() {
receivedContext = this;
}, myObject); // <-- Pass myObject as context
myObject.trigger('test');
expect(receivedContext).toBe(myObject);
expect(receivedContext.value).toBe('myObject');
});
it('should allow method to access instance properties with correct context', function() {
// Simulates the Level.minimizeGoal scenario
var events = eventEmitter.createEvents();
var level = {
mainVis: { name: 'visualization' },
_events: events._events,
on: events.on.bind(events),
trigger: events.trigger.bind(events),
minimizeGoal: function() {
return this.mainVis;
}
};
var result = null;
level.on('minimizeCanvas', function() {
result = this.mainVis;
}, level); // <-- Pass level as context
level.trigger('minimizeCanvas');
expect(result).toBe(level.mainVis);
expect(result.name).toBe('visualization');
});
it('should fail to access instance properties without correct context', function() {
// Demonstrates what happens WITHOUT the fix
var events = eventEmitter.createEvents();
var level = {
mainVis: { name: 'visualization' },
_events: events._events,
on: events.on.bind(events),
trigger: events.trigger.bind(events)
};
var result = 'not-called';
level.on('minimizeCanvas', function() {
// 'this' is EventEmitter, which doesn't have mainVis
result = this.mainVis;
}); // <-- No context passed
level.trigger('minimizeCanvas');
// mainVis is undefined on EventEmitter
expect(result).toBeUndefined();
});
});
describe('listenTo()', function() {
it('should listen to events on another object', function() {
var emitter1 = new eventEmitter.EventEmitter();
var emitter2 = new eventEmitter.EventEmitter();
var called = false;
emitter1.listenTo(emitter2, 'test', function() {
called = true;
});
emitter2.trigger('test');
expect(called).toBe(true);
});
it('should use listener as context', function() {
var emitter1 = new eventEmitter.EventEmitter();
var emitter2 = new eventEmitter.EventEmitter();
var receivedContext = null;
emitter1.listenTo(emitter2, 'test', function() {
receivedContext = this;
});
emitter2.trigger('test');
expect(receivedContext).toBe(emitter1);
});
});
describe('stopListening()', function() {
it('should stop listening to all events', function() {
var emitter1 = new eventEmitter.EventEmitter();
var emitter2 = new eventEmitter.EventEmitter();
var count = 0;
emitter1.listenTo(emitter2, 'test', function() {
count++;
});
emitter2.trigger('test');
expect(count).toBe(1);
emitter1.stopListening();
emitter2.trigger('test');
expect(count).toBe(1);
});
});
describe('multiple events', function() {
it('should handle space-separated event names in on()', function() {
var emitter = new eventEmitter.EventEmitter();
var count = 0;
emitter.on('event1 event2', function() {
count++;
});
emitter.trigger('event1');
emitter.trigger('event2');
expect(count).toBe(2);
});
});
// Tests for bugs found during Backbone removal
describe('off() with context (bug fix)', function() {
it('should remove listener when callback and context both match', function() {
var emitter = new eventEmitter.EventEmitter();
var context1 = { name: 'context1' };
var count = 0;
var callback = function() { count++; };
emitter.on('test', callback, context1);
emitter.off('test', callback, context1);
emitter.trigger('test');
expect(count).toBe(0);
});
it('should keep listener when callback matches but context differs', function() {
var emitter = new eventEmitter.EventEmitter();
var context1 = { name: 'context1' };
var context2 = { name: 'context2' };
var count = 0;
var callback = function() { count++; };
emitter.on('test', callback, context1);
emitter.off('test', callback, context2); // Different context
emitter.trigger('test');
expect(count).toBe(1); // Should still fire
});
it('should remove listener when only callback specified (no context check)', function() {
var emitter = new eventEmitter.EventEmitter();
var context1 = { name: 'context1' };
var count = 0;
var callback = function() { count++; };
emitter.on('test', callback, context1);
emitter.off('test', callback); // No context = remove regardless of context
emitter.trigger('test');
expect(count).toBe(0);
});
it('should handle multiple listeners with same callback different contexts', function() {
var emitter = new eventEmitter.EventEmitter();
var context1 = { name: 'context1' };
var context2 = { name: 'context2' };
var results = [];
var callback = function() { results.push(this.name); };
emitter.on('test', callback, context1);
emitter.on('test', callback, context2);
emitter.off('test', callback, context1); // Only remove context1
emitter.trigger('test');
expect(results).toEqual(['context2']);
});
});
describe('Sandbox/Level event pattern', function() {
it('should simulate Sandbox event setup with bound methods', function() {
// This simulates how Sandbox sets up events
var events = eventEmitter.createEvents();
function Sandbox() {
this._events = events._events;
this.on = events.on.bind(events);
this.off = events.off.bind(events);
this.trigger = events.trigger.bind(events);
this.mainVis = { myResize: function() { return 'resized'; } };
}
var sandbox = new Sandbox();
var result = null;
// BUG: Without context, 'this' is EventEmitter
sandbox.on('resize', function() {
result = this.mainVis;
});
sandbox.trigger('resize');
expect(result).toBeUndefined(); // mainVis not found on EventEmitter
});
it('should work correctly when context is passed (the fix)', function() {
var events = eventEmitter.createEvents();
function Sandbox() {
this._events = events._events;
this.on = events.on.bind(events);
this.off = events.off.bind(events);
this.trigger = events.trigger.bind(events);
this.mainVis = { myResize: function() { return 'resized'; } };
}
var sandbox = new Sandbox();
var result = null;
// FIX: Pass 'this' as context
sandbox.on('resize', function() {
result = this.mainVis.myResize();
}, sandbox);
sandbox.trigger('resize');
expect(result).toBe('resized');
});
it('should simulate Level extending Sandbox with proper event binding', function() {
var events = eventEmitter.createEvents();
function Level() {
this._events = events._events;
this.on = events.on.bind(events);
this.off = events.off.bind(events);
this.trigger = events.trigger.bind(events);
this.mainVis = { myResize: function() { return 'main resized'; } };
this.goalVis = { hide: function() { return 'goal hidden'; } };
// FIX: Always pass 'this' as context
this.on('minimizeCanvas', this.minimizeGoal, this);
this.on('resizeCanvas', this.resizeGoal, this);
}
Level.prototype.minimizeGoal = function() {
return this.goalVis.hide();
};
Level.prototype.resizeGoal = function() {
return this.mainVis.myResize();
};
var level = new Level();
var minimizeResult = null;
var resizeResult = null;
// Override to capture results
level.on('minimizeCanvas', function() {
minimizeResult = this.goalVis.hide();
}, level);
level.on('resizeCanvas', function() {
resizeResult = this.mainVis.myResize();
}, level);
level.trigger('minimizeCanvas');
level.trigger('resizeCanvas');
expect(minimizeResult).toBe('goal hidden');
expect(resizeResult).toBe('main resized');
});
});
describe('Event cleanup in die() pattern', function() {
it('should properly unbind events before destroying objects', function() {
var events = eventEmitter.createEvents();
var callCount = 0;
function Level() {
this._events = events._events;
this.on = events.on.bind(events);
this.off = events.off.bind(events);
this.trigger = events.trigger.bind(events);
this.mainVis = { myResize: function() { callCount++; } };
this.resizeHandler = function() {
this.mainVis.myResize();
};
this.on('resize', this.resizeHandler, this);
}
Level.prototype.die = function() {
// CORRECT: Unbind BEFORE deleting
this.off('resize');
delete this.mainVis;
};
var level = new Level();
level.trigger('resize');
expect(callCount).toBe(1);
level.die();
level.trigger('resize'); // Should not throw or increment
expect(callCount).toBe(1);
});
it('should throw if events fire after objects deleted without cleanup', function() {
var events = eventEmitter.createEvents();
function Level() {
this._events = events._events;
this.on = events.on.bind(events);
this.off = events.off.bind(events);
this.trigger = events.trigger.bind(events);
this.mainVis = { myResize: function() {} };
this.on('resize', function() {
this.mainVis.myResize(); // Will throw if mainVis deleted
}, this);
}
Level.prototype.badDie = function() {
// BUG: Delete without unbinding
delete this.mainVis;
};
var level = new Level();
level.badDie();
expect(function() {
level.trigger('resize');
}).toThrow();
});
});
describe('CanvasTerminalHolder parent.trigger pattern', function() {
it('should trigger events on parent with correct context', function() {
var events = eventEmitter.createEvents();
function Level() {
this._events = events._events;
this.on = events.on.bind(events);
this.off = events.off.bind(events);
this.trigger = events.trigger.bind(events);
this.mainVis = { myResize: function() { return 'resized'; } };
this.goalVis = { hide: function() { return 'hidden'; } };
this.results = [];
this.on('minimizeCanvas', this.minimizeGoal, this);
}
Level.prototype.minimizeGoal = function(position, size) {
this.results.push('minimizeGoal called');
this.results.push('goalVis: ' + this.goalVis.hide());
this.results.push('position: ' + JSON.stringify(position));
};
function CanvasTerminalHolder(parent) {
this.parent = parent;
}
CanvasTerminalHolder.prototype.minimize = function() {
this.parent.trigger('minimizeCanvas', { left: 100, top: 50 }, { width: 200, height: 300 });
};
var level = new Level();
var holder = new CanvasTerminalHolder(level);
holder.minimize();
expect(level.results).toContain('minimizeGoal called');
expect(level.results).toContain('goalVis: hidden');
expect(level.results).toContain('position: {"left":100,"top":50}');
});
});
});
================================================
FILE: __tests__/git.spec.js
================================================
var intl = require('../src/js/intl')
var base = require('./base');
var expectTreeAsync = base.expectTreeAsync;
var runCommand = base.runCommand;
describe('Git', function() {
it('Commits', function() {
return expectTreeAsync(
'git commit',
base.ONE_COMMIT_TREE
);
});
it('handles commit options', function() {
return expectTreeAsync(
'git commit; git commit --amend;',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('throws with bad arg options', function() {
return expectTreeAsync(
'git commit foo; git commit -am -m; git commit -am -a; git commit -am "hi" "ho"; git commit',
base.ONE_COMMIT_TREE
);
});
it('handles lower case branch options', function() {
return expectTreeAsync(
'git branch banana c0; git commit; git checkout -b side banana; git branch -d banana;git branch -f another c1; git commit',
'{"branches":{"main":{"target":"C2","id":"main"},"side":{"target":"C3","id":"side"},"another":{"target":"C1","id":"another"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C0"],"id":"C3"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('handles branch options', function() {
return expectTreeAsync(
'git branch banana C0; git commit; git checkout -b side banana; git branch -d banana;git branch -f another C1; git commit',
'{"branches":{"main":{"target":"C2","id":"main"},"side":{"target":"C3","id":"side"},"another":{"target":"C1","id":"another"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C0"],"id":"C3"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('does add', function() {
return expectTreeAsync(
'git add; git commit',
base.ONE_COMMIT_TREE
);
});
it('resets with all options', function() {
return expectTreeAsync(
'git commit;git reset --soft HEAD~1;git reset --hard HEAD~1;gc;go C1;git reset --hard C3;',
'{"branches":{"main":{"target":"C3","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"C1","id":"HEAD"}}'
);
});
it('Checkouts', function() {
return expectTreeAsync(
'git checkout -b side',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
describe('Switches', function() {
it("to a commit", function () {
return expectTreeAsync(
'git switch C0',
'{"branches":{"main":{"target":"C1","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"C0","id":"HEAD"}}'
);
});
it("to a branch", function () {
return expectTreeAsync(
'git switch side',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('to a branch with -c option', function() {
return expectTreeAsync(
'git switch -c side',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('to a branch with --create option', function() {
return expectTreeAsync(
'git switch --create side',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('to a branch with -C option', function() {
return expectTreeAsync(
'git switch -C side',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('to a branch with -C option and given base branch', function() {
return expectTreeAsync(
'git switch -C side main',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"C0","id":"HEAD"}}'
);
});
it('to a branch with --force-create option', function() {
return expectTreeAsync(
'git switch --force-create side',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('to a branch with --force-create option and given base branch', function() {
return expectTreeAsync(
'git switch --force-create side main',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C1","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"C0","id":"HEAD"}}'
);
});
});
it('Rebases', function() {
return expectTreeAsync(
'gc; git checkout -b side C1; gc; git rebase main',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('Interactive rebase', function() {
return expectTreeAsync(
'gc; git checkout -b side C1; gc; git rebase -i main --interactive-test',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('Interactive rebases with commit re-ordering', function() {
return expectTreeAsync(
'gc;gc;git rebase -i C0 --interactive-test C3,C1',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22main%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C3%27%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22id%22%3A%22HEAD%22%2C%22target%22%3A%22main%22%7D%7D'
);
});
it('Rebases reordered commits in their new order', function() {
return expectTreeAsync(
'gc;gc;gc;gc;git branch one C1;git branch two C1;git branch three C1;git rebase C2 three;git checkout one;git cherry-pick C4 C3 C2; git checkout two; git cherry-pick C5; git rebase two one',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C5%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22one%22%3A%7B%22target%22%3A%22C2%27%27%22%2C%22id%22%3A%22one%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22two%22%3A%7B%22target%22%3A%22C5%27%22%2C%22id%22%3A%22two%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22three%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22three%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C4%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C4%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C4%27%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C4%27%22%5D%2C%22id%22%3A%22C3%27%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C2%27%22%7D%2C%22C5%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C5%27%22%7D%2C%22C4%27%27%22%3A%7B%22parents%22%3A%5B%22C5%27%22%5D%2C%22id%22%3A%22C4%27%27%22%7D%2C%22C3%27%27%22%3A%7B%22parents%22%3A%5B%22C4%27%27%22%5D%2C%22id%22%3A%22C3%27%27%22%7D%2C%22C2%27%27%22%3A%7B%22parents%22%3A%5B%22C3%27%27%22%5D%2C%22id%22%3A%22C2%27%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22one%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
})
it('Switch branch and execute interactive rebase', function() {
return expectTreeAsync(
'git branch test;git rebase -i C0 test --interactive-test',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22main%22%7D%2C%22test%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22test%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22id%22%3A%22HEAD%22%2C%22target%22%3A%22test%22%7D%7D'
);
});
it('Reverts', function() {
return expectTreeAsync(
'git revert HEAD',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22main%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('Merges', function() {
return expectTreeAsync(
'gc; git checkout -b side C1; gc; git merge main',
'{"branches":{"main":{"target":"C2","id":"main"},"side":{"target":"C4","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('Resets', function() {
return expectTreeAsync(
'git commit; git reset HEAD~1',
'{"branches":{"main":{"target":"C1","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('Branches', function() {
return expectTreeAsync(
'git branch side C0',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('Branches lowercase', function() {
return expectTreeAsync(
'git branch side c0',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('Deletes branches', function() {
return expectTreeAsync(
'git branch side; git branch -d side',
'{"branches":{"main":{"target":"C1","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('Amends commits', function() {
return expectTreeAsync(
'git commit --amend',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22main%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('Cherry picks', function() {
return expectTreeAsync(
'git checkout -b side C0; gc; git cherry-pick C11; git cherry-pick C1',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C1%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C1%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('Range operator is not supported', function() {
return expectTreeAsync(
'git checkout -b side C0; git cherry-pick C1..C0',
'{"branches":{"main":{"target": "C1","id": "main"},"side":{"target":"C0","id": "side"}},"commits":{"C0":{"parents":[],"id": "C0","rootCommit": true},"C1":{"parents":["C0"],"id": "C1"}},"HEAD":{"id": "HEAD","target":"side"}}'
);
});
it('Forces branches', function() {
return expectTreeAsync(
'git checkout -b side; git branch -f side C0',
'{"branches":{"main":{"target":"C1","id":"main"},"side":{"target":"C0","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('Rebases only new commits to destination', function() {
return expectTreeAsync(
'git checkout -b side C0; gc; gc;git cherry-pick C1;git rebase main',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22side%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C1%27%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%27%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%27%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('checks out after a rebase', function() {
return expectTreeAsync(
'git commit; git checkout -b bugFix C1; git commit; git rebase main;git checkout main',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22bugFix%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22bugFix%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('switches after a rebase ', function() {
return expectTreeAsync(
'git commit; git switch -c bugFix C1; git commit; git rebase main;git switch main',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22bugFix%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22bugFix%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('checks out after an interactive rebase', function() {
return expectTreeAsync(
'git commit; git checkout -b bugFix C1; git commit; git rebase -i main --interactive-test;git checkout main',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22bugFix%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22bugFix%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('solves merging level', function() {
return expectTreeAsync(
'git checkout -b bugFix;git commit;git checkout main;git commit;git merge bugFix',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C4%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22bugFix%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22bugFix%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C2%22%2C%22C3%22%5D%2C%22id%22%3A%22C4%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('solves rebase level', function() {
return expectTreeAsync(
'git checkout -b bugFix;git commit;git checkout main;git commit;git checkout bugFix;git rebase main',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22bugFix%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22bugFix%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22bugFix%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('solves rebase level with interactive rebase', function() {
return expectTreeAsync(
'git checkout -b bugFix;git commit;git checkout main;git commit;git checkout bugFix;git rebase -i main --interactive-test',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22bugFix%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22bugFix%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22bugFix%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('does a whole bunch of crazy merging', function() {
return expectTreeAsync(
'gc;go -b side C1;gc;git merge main;go -b bug main;gc;go -b wut C3;git rebase bug;go side;git rebase wut;gc;git rebase wut;git merge C4;go main;git rebase side;go C6;git merge C3\';gb -f wut C8;go bug;git rebase wut',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C7%22%2C%22id%22%3A%22main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C7%22%2C%22id%22%3A%22side%22%7D%2C%22bug%22%3A%7B%22target%22%3A%22C8%22%2C%22id%22%3A%22bug%22%7D%2C%22wut%22%3A%7B%22target%22%3A%22C8%22%2C%22id%22%3A%22wut%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C2%22%2C%22C3%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C5%22%5D%2C%22id%22%3A%22C3%27%22%7D%2C%22C6%22%3A%7B%22parents%22%3A%5B%22C4%22%5D%2C%22id%22%3A%22C6%22%7D%2C%22C6%27%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C6%27%22%7D%2C%22C7%22%3A%7B%22parents%22%3A%5B%22C4%22%2C%22C6%27%22%5D%2C%22id%22%3A%22C7%22%7D%2C%22C8%22%3A%7B%22parents%22%3A%5B%22C3%27%22%2C%22C6%22%5D%2C%22id%22%3A%22C8%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22bug%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('if no-ff is specified, will always make a merge commit', function() {
return expectTreeAsync(
'git commit; go -b side HEAD~1; git commit; git merge main; go main; git merge side --no-ff',
'{"branches":{"main":{"target":"C5","id":"main","remoteTrackingBranchID":null},"side":{"target":"C4","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"},"C5":{"parents":["C2","C4"],"id":"C5"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('if squash is specified, will always make a squash merge commit', function() {
return expectTreeAsync(
'git commit; go -b side HEAD~1; git commit; git merge main; go main; git merge side --squash',
'{"branches":{"main":{"target":"C5","id":"main","remoteTrackingBranchID":null},"side":{"target":"C4","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"},"C5":{"parents":["C2"],"id":"C5"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('makes a tag', function() {
return expectTreeAsync(
'git tag v1',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{"v1":{"target":"C1","id":"v1","type":"tag"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('makes a tag and removes a tag', function() {
return expectTreeAsync(
'git tag v1; git tag -d v1',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('makes a tag on another ref', function() {
return expectTreeAsync(
'git tag v1 C0',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{"v1":{"target":"C0","id":"v1","type":"tag"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('doesn\'t make a tag if ref doesn\'t resolve', function() {
return expectTreeAsync(
'git tag v1 foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('makes tag with relative ref and etc', function() {
return expectTreeAsync(
'git tag v1 HEAD~1',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{"v1":{"target":"C0","id":"v1","type":"tag"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('makes tag with 3 letters', function() {
return expectTreeAsync(
'git tag foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{"foo":{"target":"C1","id":"foo","type":"tag"}},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('does not make tag if ref does not resolve', function() {
return expectTreeAsync(
'git tag foo banana',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('should respect second command for -B option', function() {
return expectTreeAsync(
'git commit; git checkout -B side C1',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"side":{"target":"C1","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"side","id":"HEAD"}}'
);
});
it('will throw error if bad commits given to interactive test', function() {
return expectTreeAsync(
'gc; git rebase HEAD~2 -i --interactive-test C2,C100; gc',
'{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
it('can handle slashes and dashes in branch names but doesn\'t allow o/', function() {
return expectTreeAsync(
'git branch foo/bar; git commit; git checkout foo/bar; gc; go main; git merge foo/bar; go foo/bar; git checkout -b bar-baz; git commit; git branch o/foo',
'{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null},"foo/bar":{"target":"C3","id":"foo/bar","remoteTrackingBranchID":null},"bar-baz":{"target":"C5","id":"bar-baz","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"},"C5":{"parents":["C3"],"id":"C5"}},"tags":{},"HEAD":{"target":"bar-baz","id":"HEAD"}}'
);
});
it('the regex allows for multiple dashes but not in a row', function() {
return expectTreeAsync(
'git branch foo-bar-banana-baz; gc; git branch foo----bar//baz',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"foo-bar-b":{"target":"C1","id":"foo-bar-b","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}'
);
});
describe('RevList', function() {
it('requires at least 1 argument', function() {
return runCommand('git rev-list', function(commandMsg) {
expect(commandMsg).toEqual(intl.str(
'git-error-args-few',
{
lower: 1,
what: 'with git rev-list'
}
));
});
});
describe('supports', function() {
var SETUP = 'git co -b left C0; gc; git merge main; git co -b right C0; gc; git merge main; git co -b all left; git merge right; ';
it('single included revision', function() {
return runCommand(SETUP + 'git rev-list all', function(commandMsg) {
expect(commandMsg).toBe('C6\nC5\nC4\nC3\nC2\nC1\nC0\n');
});
});
it('single excluded revision', function() {
return runCommand(SETUP + 'git rev-list all ^right', function(commandMsg) {
expect(commandMsg).toBe('C6\nC3\nC2\n');
});
});
it('multiple included revisions', function() {
return runCommand(SETUP + 'git rev-list right left', function(commandMsg) {
expect(commandMsg).toBe('C5\nC4\nC3\nC2\nC1\nC0\n');
});
});
it('multiple excluded revisions', function() {
return runCommand(SETUP + 'git rev-list all ^right ^left', function(commandMsg) {
expect(commandMsg).toBe('C6\n');
});
});
it('range between branches', function() {
return runCommand(SETUP + 'git rev-list left..right', function(commandMsg) {
expect(commandMsg).toBe('C5\nC4\n');
});
});
it('range between commits', function() {
return runCommand(SETUP + 'git rev-list C3..C5', function(commandMsg) {
expect(commandMsg).toBe('C5\nC4\n');
});
});
});
});
describe('Log supports', function() {
var SETUP = 'git co -b left C0; gc; git merge main; git co -b right C0; gc; git merge main; git co -b all left; git merge right; ';
it('implied HEAD', function() {
return runCommand(SETUP + '; git co right; git log', function(commandMsg) {
expect(commandMsg).toContain('Commit: C0\n');
expect(commandMsg).toContain('Commit: C1\n');
expect(commandMsg).not.toContain('Commit: C2\n');
expect(commandMsg).not.toContain('Commit: C3\n');
expect(commandMsg).toContain('Commit: C4\n');
expect(commandMsg).toContain('Commit: C5\n');
expect(commandMsg).not.toContain('Commit: C6\n');
});
});
it('single included revision', function() {
return runCommand(SETUP + 'git log right', function(commandMsg) {
expect(commandMsg).toContain('Commit: C0\n');
expect(commandMsg).toContain('Commit: C1\n');
expect(commandMsg).not.toContain('Commit: C2\n');
expect(commandMsg).not.toContain('Commit: C3\n');
expect(commandMsg).toContain('Commit: C4\n');
expect(commandMsg).toContain('Commit: C5\n');
expect(commandMsg).not.toContain('Commit: C6\n');
});
});
it('single excluded revision', function() {
return runCommand(SETUP + 'git log all ^right', function(commandMsg) {
expect(commandMsg).not.toContain('Commit: C0\n');
expect(commandMsg).not.toContain('Commit: C1\n');
expect(commandMsg).toContain('Commit: C2\n');
expect(commandMsg).toContain('Commit: C3\n');
expect(commandMsg).not.toContain('Commit: C4\n');
expect(commandMsg).not.toContain('Commit: C5\n');
expect(commandMsg).toContain('Commit: C6\n');
});
});
it('multiple included revisions', function() {
return runCommand(SETUP + 'git log right left', function(commandMsg) {
expect(commandMsg).toContain('Commit: C0\n');
expect(commandMsg).toContain('Commit: C1\n');
expect(commandMsg).toContain('Commit: C2\n');
expect(commandMsg).toContain('Commit: C3\n');
expect(commandMsg).toContain('Commit: C4\n');
expect(commandMsg).toContain('Commit: C5\n');
expect(commandMsg).not.toContain('Commit: C6\n');
});
});
it('multiple excluded revisions', function() {
return runCommand(SETUP + 'git log all ^right ^left', function(commandMsg) {
expect(commandMsg).not.toContain('Commit: C0\n');
expect(commandMsg).not.toContain('Commit: C1\n');
expect(commandMsg).not.toContain('Commit: C2\n');
expect(commandMsg).not.toContain('Commit: C3\n');
expect(commandMsg).not.toContain('Commit: C4\n');
expect(commandMsg).not.toContain('Commit: C5\n');
expect(commandMsg).toContain('Commit: C6\n');
});
});
});
describe ('Git rebase onto', function () {
it('rebase onto with two arguments', function() {
return expectTreeAsync(
'git commit; git commit; git switch -c F1 main~2; git commit; git commit; git switch -c F2; git commit; git commit; git rebase --onto main F1;',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22F1%22%3A%7B%22target%22%3A%22C5%22%2C%22id%22%3A%22F1%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22F2%22%3A%7B%22target%22%3A%22C7%27%22%2C%22id%22%3A%22F2%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C4%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C6%22%3A%7B%22parents%22%3A%5B%22C5%22%5D%2C%22id%22%3A%22C6%22%7D%2C%22C7%22%3A%7B%22parents%22%3A%5B%22C6%22%5D%2C%22id%22%3A%22C7%22%7D%2C%22C6%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C6%27%22%7D%2C%22C7%27%22%3A%7B%22parents%22%3A%5B%22C6%27%22%5D%2C%22id%22%3A%22C7%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22F2%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('rebase onto with three arguments', function() {
return expectTreeAsync(
'git commit; git commit; git switch -c F1 main~2; git commit; git commit; git switch -c F2; git commit; git commit; git checkout C1; git rebase --onto main F1 F2;',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22F1%22%3A%7B%22target%22%3A%22C5%22%2C%22id%22%3A%22F1%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22F2%22%3A%7B%22target%22%3A%22C7%27%22%2C%22id%22%3A%22F2%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C4%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C6%22%3A%7B%22parents%22%3A%5B%22C5%22%5D%2C%22id%22%3A%22C6%22%7D%2C%22C7%22%3A%7B%22parents%22%3A%5B%22C6%22%5D%2C%22id%22%3A%22C7%22%7D%2C%22C6%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C6%27%22%7D%2C%22C7%27%22%3A%7B%22parents%22%3A%5B%22C6%27%22%5D%2C%22id%22%3A%22C7%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22F2%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
it('rebase onto fast forward', function() {
return expectTreeAsync(
'git switch -c F1; git commit; git rebase --onto F1 main;',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"F1":{"target":"C2","id":"F1","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"F1","id":"HEAD"}}'
);
});
});
});
================================================
FILE: __tests__/levels.spec.js
================================================
var base = require('./base');
describe('GitEngine Levels', function() {
var sequences = require('../src/levels/index').levelSequences;
Object.keys(sequences).forEach(function(sequenceKey) {
var levels = sequences[sequenceKey];
Object.keys(levels).forEach(function(index) {
var levelBlob = levels[index];
it('solves level ' + levelBlob['name']['en_US'] + ' in sequence ' + sequenceKey, function() {
base.expectLevelSolved(levelBlob);
});
}.bind(this));
});
});
================================================
FILE: __tests__/mercurial.spec.js
================================================
var base = require('./base');
var expectTreeAsync = base.expectTreeAsync;
describe('Mercurial', function() {
var assert = function(msg, command, tree) {
it(msg, function() {
return expectTreeAsync(command, tree);
});
};
assert(
'Commits',
'hg commit',
base.ONE_COMMIT_TREE
);
assert(
'Makes a bookmark',
'hg book;hg book foo;hg ci;hg book -r C0 asd;',
'{"branches":{"main":{"target":"C1","id":"main"},"foo":{"target":"C2","id":"foo"},"asd":{"target":"C0","id":"asd"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"}}'
);
assert(
'updates to bookmarks',
'hg book;hg book foo;hg ci;hg book -r C0 asd; hg update asd',
'{"branches":{"main":{"target":"C1","id":"main"},"foo":{"target":"C2","id":"foo"},"asd":{"target":"C0","id":"asd"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"asd","id":"HEAD"}}'
);
assert(
'updates to revisions',
'hg update -r C0',
'{"branches":{"main":{"target":"C1","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"C0","id":"HEAD"}}'
);
assert(
'backs out revisions and bookmarks',
'hg book -r C0 foo;hg ci;hg backout foo;hg backout -r C1 C2;',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%7D%2C%22foo%22%3A%7B%22target%22%3A%22C0%22%2C%22id%22%3A%22foo%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C0%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C0%27%22%7D%2C%22C1%27%22%3A%7B%22parents%22%3A%5B%22C0%27%22%5D%2C%22id%22%3A%22C1%27%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C1%27%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
assert(
'commits and amends',
'hg commit -A; hg commit --amend',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
assert(
'rebases with -d',
'hg ci; hg book -r C1 feature; hg update feature; hg ci;hg book debug;hg ci;hg rebase -d main;',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22feature%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22feature%22%7D%2C%22debug%22%3A%7B%22target%22%3A%22C4%27%22%2C%22id%22%3A%22debug%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%2C%22C4%27%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C4%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22debug%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
assert(
'rebases with -d below stuff',
'hg ci; hg book -r C1 feature; hg update feature; hg ci;hg book -r C3 debug;hg ci;hg up debug;hg rebase -d main -b .;',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22main%22%7D%2C%22feature%22%3A%7B%22target%22%3A%22C4%22%2C%22id%22%3A%22feature%22%7D%2C%22debug%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22debug%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22debug%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
assert(
'grafts commits down',
'hg book foo;hg commit; hg update main; hg commit;hg graft -r C2;hg update foo; hg graft -r C3',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22foo%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22foo%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C2%27%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22foo%22%2C%22id%22%3A%22HEAD%22%7D%7D'
);
});
================================================
FILE: __tests__/remote.spec.js
================================================
var base = require('./base');
var intl = require('../src/js/intl');
var expectTreeAsync = base.expectTreeAsync;
var runCommand = base.runCommand;
describe('Git Remotes', function() {
it('clones', function() {
return expectTreeAsync(
'git clone',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does fake teamwork', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does fake teamwork and then fetches', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git fetch',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls', function() {
return expectTreeAsync(
'git clone; git commit; git fakeTeamwork; git pull',
'{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C3","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls with rebase', function() {
return expectTreeAsync(
'git clone; git commit; git fakeTeamwork; git pull --rebase',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3A%22o/main%22%2C%22localBranchesThatTrackThis%22%3Anull%7D%2C%22o/main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22o/main%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3A%5B%22main%22%5D%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%2C%22originTree%22%3A%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D%7D'
);
});
it('pulls with rebase and arguments', function() {
return expectTreeAsync(
'git checkout -b foo; git clone; git fakeTeamwork foo; git commit; git pull --rebase origin foo',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3A%22o/main%22%7D%2C%22foo%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22foo%22%2C%22remoteTrackingBranchID%22%3A%22o/foo%22%7D%2C%22o/main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22o/main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22o/foo%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22o/foo%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22foo%22%2C%22id%22%3A%22HEAD%22%7D%2C%22originTree%22%3A%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22foo%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22foo%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22foo%22%2C%22id%22%3A%22HEAD%22%7D%7D%7D',
);
});
it('pushes', function() {
return expectTreeAsync(
'git clone; git commit; git push',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls and then pushes', function() {
return expectTreeAsync(
'git clone; git commit; git fakeTeamwork; git pull; git push',
'{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C4","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C3":{"parents":["C1"],"id":"C3"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls with rebase and then pushes', function() {
return expectTreeAsync(
'git clone; git commit; git fakeTeamwork; git pull --rebase; git push',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3A%22o/main%22%2C%22localBranchesThatTrackThis%22%3Anull%7D%2C%22o/main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22o/main%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3A%5B%22main%22%5D%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%2C%22originTree%22%3A%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C2%27%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C2%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D%7D'
);
});
it('clones with many branches', function() {
return expectTreeAsync(
'git branch bugFix; git clone',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"bugFix":{"target":"C1","id":"bugFix","remoteTrackingBranchID":"o/bugFix"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/bugFix":{"target":"C1","id":"o/bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"bugFix":{"target":"C1","id":"bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('clones with a merge commit, does teamwork, fetches', function() {
return expectTreeAsync(
'git branch bugFix; git commit; git checkout bugFix; git commit; git merge main; git clone; git fakeTeamwork bugFix 2',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"bugFix":{"target":"C4","id":"bugFix","remoteTrackingBranchID":"o/bugFix"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"o/bugFix":{"target":"C4","id":"o/bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"bugFix","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"bugFix":{"target":"C6","id":"bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C2","C3"],"id":"C4"},"C5":{"parents":["C4"],"id":"C5"},"C6":{"parents":["C5"],"id":"C6"}},"HEAD":{"target":"bugFix","id":"HEAD"}}}'
);
});
it('only fetches one branch if specified', function() {
return expectTreeAsync(
'git branch bugFix; git clone; git fakeTeamwork bugFix; git fakeTeamwork; git fetch origin bugFix',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"bugFix":{"target":"C1","id":"bugFix","remoteTrackingBranchID":"o/bugFix"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/bugFix":{"target":"C2","id":"o/bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":null},"bugFix":{"target":"C2","id":"bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('can fetch with --force where otherwise it would error', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git commit; git checkout C1; git fetch origin main:main --force;',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C3":{"parents":["C1"],"id":"C3"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"C1","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}',
);
});
it('checks all branches for fetching', function() {
return expectTreeAsync(
'git branch bugFix; git clone; git fakeTeamwork; git fetch',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"bugFix":{"target":"C1","id":"bugFix","remoteTrackingBranchID":"o/bugFix"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"o/bugFix":{"target":"C1","id":"o/bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"bugFix":{"target":"C1","id":"bugFix","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls with nothing and then commits', function() {
return expectTreeAsync(
'git clone; git pull; git commit',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls from different remote tracking branches nad merges', function() {
return expectTreeAsync(
'git branch side; git clone; git fakeTeamwork side;git commit; git pull origin side;git pull;git fakeTeamwork main;git pull',
'{"branches":{"main":{"target":"C6","id":"main","remoteTrackingBranchID":"o/main"},"side":{"target":"C1","id":"side","remoteTrackingBranchID":"o/side"},"o/main":{"target":"C5","id":"o/main","remoteTrackingBranchID":null},"o/side":{"target":"C2","id":"o/side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C3":{"parents":["C1"],"id":"C3"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C2","C3"],"id":"C4"},"C5":{"parents":["C1"],"id":"C5"},"C6":{"parents":["C4","C5"],"id":"C6"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C5","id":"main","remoteTrackingBranchID":null},"side":{"target":"C2","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C5":{"parents":["C1"],"id":"C5"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls with rebase from different remote tracking', function() {
return expectTreeAsync(
'git branch side; git clone; git fakeTeamwork side;git commit; git pull origin side --rebase',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3A%22o/main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22side%22%2C%22remoteTrackingBranchID%22%3A%22o/side%22%7D%2C%22o/main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22o/main%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3A%5B%22main%22%5D%7D%2C%22o/side%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22o/side%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3A%5B%22side%22%5D%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%2C%22originTree%22%3A%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3Anull%7D%2C%22side%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22side%22%2C%22remoteTrackingBranchID%22%3Anull%2C%22localBranchesThatTrackThis%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22side%22%2C%22id%22%3A%22HEAD%22%7D%7D%7D'
);
});
it('pushes to another remote', function() {
return expectTreeAsync(
'git branch side; git clone;git commit; git push origin HEAD:side',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"side":{"target":"C1","id":"side","remoteTrackingBranchID":"o/side"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/side":{"target":"C2","id":"o/side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"side":{"target":"C2","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pushes to tracking remote', function() {
return expectTreeAsync(
'git branch side; git clone;git commit;git push; go side; git commit; git push',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"side":{"target":"C3","id":"side","remoteTrackingBranchID":"o/side"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"o/side":{"target":"C3","id":"o/side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"side","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"side":{"target":"C3","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('sets tracking when checking out remote branch', function() {
return expectTreeAsync(
'git clone; git checkout -b side o/main;git fakeTeamwork;git pull',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null,"localBranchesThatTrackThis":["main","side"]},"side":{"target":"C2","id":"side","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"side","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('also sets tracking when just branching', function() {
return expectTreeAsync(
'git clone; gb side o/main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null,"localBranchesThatTrackThis":["main","side"]},"side":{"target":"C1","id":"side","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('can push with colon refspec', function() {
return expectTreeAsync(
'git clone; gc; git checkout -b foo HEAD~1; git push origin main:main',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('can delete branches with colon refspec', function() {
return expectTreeAsync(
'git branch foo; git clone; git push origin :foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('can delete branches with colon refspec after fakeTeamwork', function() {
return expectTreeAsync(
'git commit; git branch foo; git clone; git fakeTeamwork; git push origin :foo',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('can delete branches with --delete flag', function() {
expectTreeAsync(
'git branch foo; git clone; git push origin --delete',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/foo"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
expectTreeAsync(
'git branch foo; git clone; git push origin --delete main:foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/foo"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
expectTreeAsync(
'git branch foo; git clone; git push --delete origin foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
return expectTreeAsync(
'git branch foo; git clone; git push origin --delete foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pushes new branch onto server', function() {
return expectTreeAsync(
'git clone; git commit; git push origin main:foo',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C2","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does not push for HEAD', function() {
return expectTreeAsync(
'git clone; git commit; git checkout C2; git push',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"C2","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does push for HEAD as a source though to a new branch', function() {
return expectTreeAsync(
'git clone; git commit; git checkout C2; git push origin HEAD:foo',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C2","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"C2","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('but it cant delete main on remote', function() {
return expectTreeAsync(
'git branch foo; git clone; git push origin :main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/foo"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will prune the origin tree when deleting branches', function() {
return expectTreeAsync(
'git checkout -b foo; git commit; git clone; git push origin :foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will not push to a remote if the local ref does not exist', function() {
return expectTreeAsync(
'git clone; git push origin foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will push to the remote branch IF IT has tracking', function() {
return expectTreeAsync(
'git clone; git checkout -b foo o/main; git commit; git push',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will push to a new remote branch if no tracking is set up', function() {
return expectTreeAsync(
'git clone; git checkout -b foo; git commit; git push',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":"o/foo"},"o/foo":{"target":"C2","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will push to the remote tracking branch WHILE NOT on branch if it is set up', function() {
return expectTreeAsync(
'git clone; git checkout -b foo o/main; git commit; go main; git push origin foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will push to a new remote branch if tracking was previously set up but remote branch was merged on origin', function() {
return expectTreeAsync(
`git clone;
git switch -c feat;
git commit;
git push;
git fakeTeamwork;
git mergeMR feat main --delete-after-merge;
git commit;
git push;`,
'{"branches":{"main":{"remoteTrackingBranchID":"o/main","target":"C1","id":"main"},"o/main":{"remoteTrackingBranchID":null,"target":"C1","id":"o/main"},"feat":{"remoteTrackingBranchID":"o/feat","target":"C5","id":"feat"},"o/feat":{"remoteTrackingBranchID":null,"target":"C5","id":"o/feat"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C5":{"parents":["C2"],"id":"C5"}},"tags":{},"HEAD":{"id":"HEAD","target":"feat"},"originTree":{"branches":{"main":{"remoteTrackingBranchID":null,"target":"C4","id":"main"},"feat":{"remoteTrackingBranchID":null,"target":"C5","id":"feat"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C3","C2"],"id":"C4"},"C5":{"parents":["C2"],"id":"C5"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('will not fetch if ref does not exist on remote', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git fetch origin foo:main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does not fetch if ref does not exist on remote with one arg', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git fetch origin foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('validates branch names when fetching', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git fetch origin main:f<>',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('fetches only one remote if specified', function() {
return expectTreeAsync(
'git clone;gc;git push origin main:banana;git fakeTeamwork banana;git fakeTeamwork main;git fetch origin banana',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/banana":{"target":"C3","id":"o/banana","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null},"banana":{"target":"C3","id":"banana","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C1"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('sets remote tracking', function() {
return expectTreeAsync(
'git clone; git branch foo; git branch -u o/main foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('sets remote tracking on current branch if not specified', function() {
return expectTreeAsync(
'git clone; git checkout -b foo; git branch -u o/main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"foo","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('fetches with no args, explicit dest args, and with just one arg', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git fetch origin main:o/main;git fakeTeamwork;git fetch;git fakeTeamwork;git fetch origin main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C4","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('doesn\'t fetch if out of sync, but will update explicit dest if specified', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; go HEAD~1; git fetch origin main:main;go main; gc; go HEAD~1; git fakeTeamwork;git fetch origin main:main',
'{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"C2","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C2"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls to the right branch and destination', function() {
return expectTreeAsync(
'git clone; git checkout -b side o/main;git fakeTeamwork;git pull origin main:o/main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"side":{"target":"C2","id":"side","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"side","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('correctly checks upstream when pushing to a remote where commits already exist but branch is not updated DAYAM dawg', function() {
return expectTreeAsync(
'git push origin main^:main',
'{"branches":{"main":{"target":"C9","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C8","id":"foo","remoteTrackingBranchID":"o/foo"},"o/main":{"target":"C5","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C5","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C1"],"id":"C4"},"C5":{"parents":["C2","C4"],"id":"C5"},"C6":{"parents":["C4"],"id":"C6"},"C7":{"parents":["C6"],"id":"C7"},"C8":{"parents":["C5","C7"],"id":"C8"},"C9":{"parents":["C5"],"id":"C9"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C5","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C5","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C1"],"id":"C4"},"C5":{"parents":["C2","C4"],"id":"C5"}},"HEAD":{"target":"main","id":"HEAD"}}}',
'{"branches":{"main":{"target":"C9","id":"main","remoteTrackingBranchID":"o/main"},"foo":{"target":"C8","id":"foo","remoteTrackingBranchID":"o/foo"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C5","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C1"],"id":"C4"},"C5":{"parents":["C2","C4"],"id":"C5"},"C6":{"parents":["C4"],"id":"C6"},"C7":{"parents":["C6"],"id":"C7"},"C8":{"parents":["C5","C7"],"id":"C8"},"C9":{"parents":["C5"],"id":"C9"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C5","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C1"],"id":"C4"},"C5":{"parents":["C2","C4"],"id":"C5"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('correctly resolves source during git fetch with params', function() {
return expectTreeAsync(
'git clone; git push origin main:foo; git fakeTeamwork foo 2; git fetch origin c2:blah',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null},"blah":{"target":"C2","id":"blah","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C3","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"foo","id":"HEAD"}}}'
);
});
it('correctly makes a new branch during fetch despite nothing to download', function() {
return expectTreeAsync(
'git clone; git push origin main:foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('correctly resolves existing commits and updates', function() {
return expectTreeAsync(
'git clone; git push origin main:foo; git fakeTeamwork foo 2; git fetch origin c2:blah;go C0; git fetch origin c2:main',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null},"blah":{"target":"C2","id":"blah","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"C0","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C3","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"foo","id":"HEAD"}}}'
);
});
it('doesn\'t let you fetch to main if you are checked out there', function() {
return expectTreeAsync(
'git clone; git push origin main:foo; git fakeTeamwork foo 2; git fetch origin c2:blah; git fetch origin foo:main',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null},"blah":{"target":"C2","id":"blah","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C3","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"foo","id":"HEAD"}}}'
);
});
it('doesn\'t let you delete branches that don\'t exist', function() {
return expectTreeAsync(
'git clone; git push origin :foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls to a new branch and then merges in that branch', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git commit; git pull origin main:bar',
'{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null},"bar":{"target":"C2","id":"bar","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C3":{"parents":["C1"],"id":"C3"},"C2":{"parents":["C1"],"id":"C2"},"C4":{"parents":["C2","C3"],"id":"C4"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('makes a new branch from pull and doesn\'t bork', function() {
return expectTreeAsync(
'git clone; git pull origin :bar',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"bar":{"target":"C1","id":"bar","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('makes the new branch on push in the right place', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git push origin main:foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"o/foo":{"target":"C1","id":"o/foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('tracks remote with -u', function() {
return expectTreeAsync(
'git clone; git branch foo; git branch -u o/main foo',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"foo":{"target":"C1","id":"foo","remoteTrackingBranchID":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does not fetch if one arg is not branch ref', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork 2; git fetch origin main~1',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('creates the branch on the fly', function() {
return expectTreeAsync(
'git clone; git commit; go -b side; git push origin side',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"side":{"target":"C2","id":"side","remoteTrackingBranchID":"o/side"},"o/side":{"target":"C2","id":"o/side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"side","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null},"side":{"target":"C2","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('does not create the o/main branch on remote', function() {
return expectTreeAsync(
'git clone; git commit; git push origin o/main',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('pulls with rebase correctly in weird situation with no rebase to do', function() {
return expectTreeAsync(
'git checkout -b side; git commit; git checkout main; git commit; git commit; git merge side; git commit; git clone; git checkout -b otherMain main^^^; git rebase side; git rebase otherMain main; git push;git pull --rebase',
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C6%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3A%22o/main%22%7D%2C%22side%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22side%22%2C%22remoteTrackingBranchID%22%3A%22o/side%22%7D%2C%22o/main%22%3A%7B%22target%22%3A%22C6%22%2C%22id%22%3A%22o/main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22o/side%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22o/side%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22otherMain%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%22otherMain%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C2%22%2C%22C4%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C6%22%3A%7B%22parents%22%3A%5B%22C5%22%5D%2C%22id%22%3A%22C6%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%27%22%7D%2C%22C4%27%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C4%27%22%7D%2C%22C6%27%22%3A%7B%22parents%22%3A%5B%22C4%27%22%5D%2C%22id%22%3A%22C6%27%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%2C%22originTree%22%3A%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C6%22%2C%22id%22%3A%22main%22%2C%22remoteTrackingBranchID%22%3Anull%7D%2C%22side%22%3A%7B%22target%22%3A%22C2%22%2C%22id%22%3A%22side%22%2C%22remoteTrackingBranchID%22%3Anull%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C2%22%2C%22C4%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C6%22%3A%7B%22parents%22%3A%5B%22C5%22%5D%2C%22id%22%3A%22C6%22%7D%7D%2C%22tags%22%3A%7B%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22main%22%2C%22id%22%3A%22HEAD%22%7D%7D%7D'
);
});
it('pulls with rebase in other weird situation with just fast forward', function() {
return expectTreeAsync(
'git clone; git fakeTeamwork; git pull --rebase',
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C2","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
/* TODO -- enable this back when we have better async tree compare, it takes too long right now
it('will correctly resolve the dependency order of commits when fetching or pushing', function() {
return expectTreeAsync(
'git clone; git commit; git commit; git commit; git checkout -b test C2; git commit; git checkout main; git push; git checkout main; git merge test; git commit; git push; git checkout test; git commit; git commit; git checkout -b feat1 main; git commit; git merge test; git checkout main; git merge test; git checkout feat1; git commit; git checkout main; git merge feat1; git push',
'{"branches":{"main":{"target":"C14","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C7","id":"o/main","remoteTrackingBranchID":null},"test":{"target":"C9","id":"test","remoteTrackingBranchID":null},"feat1":{"target":"C13","id":"feat1","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"},"C5":{"parents":["C2"],"id":"C5"},"C6":{"parents":["C4","C5"],"id":"C6"},"C7":{"parents":["C6"],"id":"C7"},"C8":{"parents":["C5"],"id":"C8"},"C9":{"parents":["C8"],"id":"C9"},"C10":{"parents":["C7"],"id":"C10"},"C11":{"parents":["C10","C9"],"id":"C11"},"C12":{"parents":["C7","C9"],"id":"C12"},"C13":{"parents":["C11"],"id":"C13"},"C14":{"parents":["C12","C13"],"id":"C14"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C7","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"},"C5":{"parents":["C2"],"id":"C5"},"C6":{"parents":["C4","C5"],"id":"C6"},"C7":{"parents":["C6"],"id":"C7"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});*/
it('uses git force to bypass upstream check', function() {
return expectTreeAsync(
'git clone; git commit; git push; go C1; git branch -f main C1; go main; git commit; git commit; go C1; git checkout -b side; git commit; go main; git merge side; git push --force',
'{"branches":{"main":{"target":"C6","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C6","id":"o/main","remoteTrackingBranchID":null},"side":{"target":"C5","id":"side","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"},"C5":{"parents":["C1"],"id":"C5"},"C6":{"parents":["C4","C5"],"id":"C6"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C6","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C5":{"parents":["C1"],"id":"C5"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C3"],"id":"C4"},"C6":{"parents":["C4","C5"],"id":"C6"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('uses --push to delete commits', function() {
return expectTreeAsync(
'git commit; git clone;git reset HEAD~1;git push --force',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('uses --push to delete commits and can push again after', function() {
return expectTreeAsync(
'git commit; git clone;git reset HEAD~1;git push --force;git commit; git push ',
'{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C3","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C3","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
describe('mergeMR/mergePR on remote', function() {
it('requires a remote', function() {
return runCommand('git mergeMR', function(commandMsg) {
expect(commandMsg).toBe(intl.str('git-error-origin-required'));
});
});
it('requires exactly 2 parameters', function() {
return Promise.all([
runCommand('git clone; git mergeMR', function(commandMsg) {
expect(commandMsg).toBe(
intl.str('git-error-args-few', {
lower: '2',
what: 'with git mergeMR',
})
);
}),
runCommand('git clone; git mergeMR feat', function(commandMsg) {
expect(commandMsg).toBe(
intl.str('git-error-args-few', {
lower: '2',
what: 'with git mergeMR',
})
);
}),
runCommand('git clone; git mergeMR a b main', function(commandMsg) {
expect(commandMsg).toBe(
intl.str('git-error-args-many', {
upper: '2',
what: 'with git mergeMR',
})
);
}),
]);
});
it('merges one remote branch into another', function() {
return expectTreeAsync(
`git clone;
git switch -c feat;
git commit;
git push;
git fakeTeamwork;
git mergeMR feat main`,
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"feat":{"target":"C2","id":"feat","remoteTrackingBranchID":"o/feat"},"o/feat":{"target":"C2","id":"o/feat","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"feat","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null},"feat":{"target":"C2","id":"feat","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C3","C2"],"id":"C4"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
it('deletes the merged remote branch after merging', function() {
return expectTreeAsync(
`git clone;
git switch -c feat;
git commit;
git push;
git fakeTeamwork;
git mergeMR feat main --delete-after-merge`,
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null},"feat":{"target":"C2","id":"feat","remoteTrackingBranchID":"o/feat"},"o/feat":{"target":"C2","id":"o/feat","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"feat","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C4","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"},"C4":{"parents":["C3","C2"],"id":"C4"}},"tags":{},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
});
});
================================================
FILE: __tests__/simpleRemote.spec.js
================================================
var base = require('./base');
var expectTreeAsync = base.expectTreeAsync;
describe('Git Remote simple', function() {
it('clones', function() {
return expectTreeAsync(
'git clone',
'{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":"o/main"},"o/main":{"target":"C1","id":"o/main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"target":"C1","id":"main","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"}}}'
);
});
});
================================================
FILE: __tests__/treeCompare.spec.js
================================================
var TreeCompare = require('../src/js/graph/treeCompare');
var loadTree = function(treeString) {
return TreeCompare.convertTreeSafe(treeString);
};
var testMethod = function(compareMethod, goalTreeString, cases, options) {
cases = cases || {};
options = options || {};
if (!options.dontCompareGoal) {
// always expect the goal to compare to itself correctly
cases[goalTreeString] = true;
}
Object.keys(cases).forEach(function(actualTree) {
var value = cases[actualTree];
var isEqual = TreeCompare.dispatch(compareMethod, goalTreeString, actualTree);
if (isEqual !== value) {
console.log('this goal tree', loadTree(goalTreeString));
console.log('did not match this tree', loadTree(actualTree));
console.log('for this value', value);
}
expect(isEqual).toBe(value);
}.bind(this));
};
describe('Tree Compare', function() {
it('will return false early if goal missing origin', function() {
testMethod(
{}, // checked for all methods
'{}', // no origin tree
{
'{"originTree":{}}': false
}, {
dontCompareGoal: true
}
);
});
it('will return false early if current missing origin', function() {
testMethod(
{}, // checked for all methods
'{"originTree":{}}',
{
'{}': false
}, {
dontCompareGoal: true
}
);
});
it('compares with considering leftover branches', function() {
testMethod(
{
compareAllBranchesAndEnforceBranchCleanup: true,
},
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"foo","id":"HEAD"}}',
{
'{"branches":{"main":{"target":"C2","id":"main","remoteTrackingBranchID":null},"foo":{"target":"C2","id":"foo","remoteTrackingBranchID":null},"randoBran":{"target":"C2","id":"randoBran","remoteTrackingBranchID":null}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"tags":{},"HEAD":{"target":"foo","id":"HEAD"}}': false,
},
);
});
it('deep compares on origin tree', function() {
testMethod(
{}, // checked for all methods so this doesn't matter
// state with originTree
'{"branches":{"main":{"target":"C1","id":"main"},"o/main":{"target":"C1","id":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"remoteTrackingBranch":null,"remote":false,"target":"C1","id":"main","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 09:58:50 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 09:58:50 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"}},"HEAD":{"target":"main","id":"HEAD","type":"general ref"}}}',
{
// different cases for origin tree too
'{"branches":{"MaIn":{"target":"C1","id":"MaIn"},"o/main":{"target":"C1","id":"o/MaIn"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"MaIn","id":"HEAD"},"originTree":{"branches":{"MaIn":{"remoteTrackingBranch":null,"remote":false,"target":"C1","id":"MaIn","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 09:58:50 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 09:58:50 GMT-0800 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"}},"HEAD":{"target":"MaIn","id":"HEAD","type":"general ref"}}}': true,
// one extra commit in origin
'{"branches":{"main":{"target":"C1","id":"main"},"o/main":{"target":"C1","id":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"remoteTrackingBranch":null,"remote":false,"target":"C2","id":"main","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 10:24:50 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 10:24:50 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"},"C2":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 10:24:55 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C2"}},"HEAD":{"target":"main","id":"HEAD","type":"general ref"}}}': false,
// extra commit local
'{"branches":{"main":{"target":"C2","id":"main"},"o/main":{"target":"C1","id":"o/main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"remoteTrackingBranch":null,"remote":false,"target":"C1","id":"main","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:31:20 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:31:20 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"}},"HEAD":{"target":"main","id":"HEAD","type":"general ref"}}}': false
}
);
});
it('uses originCompare for the origin comparison results', function() {
testMethod({
// compare all branches and head locally, but only main remotely
originCompare: {
compareOnlyMain: true
}
},
// start with a local / origin with two branches
'{"branches":{"main":{"target":"C3","id":"main"},"side":{"target":"C2","id":"side"},"o/main":{"target":"C3","id":"o/main"},"o/side":{"target":"C2","id":"o/side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"remoteTrackingBranch":null,"remote":false,"target":"C3","id":"main","type":"branch"},"side":{"remoteTrackingBranch":null,"remote":false,"target":"C2","id":"side","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"},"C2":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C2"},"C3":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C3"}},"HEAD":{"target":"main","id":"HEAD","type":"general ref"}}}',
{
// committing remotely on other branch is fine
'{"branches":{"main":{"target":"C3","id":"main"},"side":{"target":"C2","id":"side"},"o/main":{"target":"C3","id":"o/main"},"o/side":{"target":"C2","id":"o/side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"remoteTrackingBranch":null,"remote":false,"target":"C3","id":"main","type":"branch"},"side":{"remoteTrackingBranch":null,"remote":false,"target":"C4","id":"side","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"},"C2":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C2"},"C3":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C3"},"C4":{"type":"commit","parents":["C2"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:35:38 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C4"}},"HEAD":{"target":"side","id":"HEAD","type":"general ref"}}}': true,
// but committing on main is not ok
'{"branches":{"main":{"target":"C3","id":"main"},"side":{"target":"C2","id":"side"},"o/main":{"target":"C3","id":"o/main"},"o/side":{"target":"C2","id":"o/side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C1"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"},"originTree":{"branches":{"main":{"remoteTrackingBranch":null,"remote":false,"target":"C5","id":"main","type":"branch"},"side":{"remoteTrackingBranch":null,"remote":false,"target":"C4","id":"side","type":"branch"}},"commits":{"C0":{"type":"commit","parents":[],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C0","rootCommit":true},"C1":{"type":"commit","parents":["C0"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C1"},"C2":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C2"},"C3":{"type":"commit","parents":["C1"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:34:45 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C3"},"C4":{"type":"commit","parents":["C2"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:35:38 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C4"},"C5":{"type":"commit","parents":["C3"],"author":"Peter Cottle","createTime":"Wed Jul 24 2013 12:37:44 GMT-0700 (PDT)","commitMessage":"Quick commit. Go Bears!","id":"C5"}},"HEAD":{"target":"main","id":"HEAD","type":"general ref"}}}': false
}
);
});
it('has default behavior to check all branches and main', function() {
testMethod(
{}, // default method, which is compare all branches and HEAD
"{\"branches\":{\"main\":{\"target\":\"C3\",\"id\":\"main\"}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"},\"C2\":{\"parents\":[\"C1\"],\"id\":\"C2\"},\"C3\":{\"parents\":[\"C2\"],\"id\":\"C3\"}},\"HEAD\":{\"target\":\"main\",\"id\":\"HEAD\"}}",
{
// side branch that is checked out
'{"branches":{"main":{"target":"C2","id":"main"},"side":{"target":"C3","id":"side"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"side","id":"HEAD"}}': false,
// different cases of branches
'{"branches":{"mAiN":{"target":"C3","id":"mAiN"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"mAiN","id":"HEAD"}}': true,
// head detached
'{"branches":{"main":{"target":"C3","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"C2","id":"HEAD"}}': false
}
);
});
it('compares only main', function() {
testMethod(
{ compareOnlyMain: true },
"{\"branches\":{\"main\":{\"target\":\"C3\",\"id\":\"main\"}},\"commits\":{\"C0\":{\"parents\":[],\"id\":\"C0\",\"rootCommit\":true},\"C1\":{\"parents\":[\"C0\"],\"id\":\"C1\"},\"C2\":{\"parents\":[\"C1\"],\"id\":\"C2\"},\"C3\":{\"parents\":[\"C2\"],\"id\":\"C3\"}},\"HEAD\":{\"target\":\"main\",\"id\":\"HEAD\"}}",
{
// side branch that is checked out
'{"branches":{"main":{"target":"C3","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"main","id":"HEAD"}}': true,
// head detached
'{"branches":{"main":{"target":"C3","id":"main"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"},"C3":{"parents":["C2"],"id":"C3"}},"HEAD":{"target":"C2","id":"HEAD"}}': true
}
);
});
it('compares all branches with hash agnostic', function() {
var selectiveRebaseGoal = require('../src/levels/rebase/selectiveRebase').level.goalTreeString;
testMethod(
{ compareAllBranchesHashAgnostic: true },
selectiveRebaseGoal,
{
// almost done with level
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C5%22%2C%22id%22%3A%22main%22%7D%2C%22one%22%3A%7B%22target%22%3A%22C2%27%5E4%22%2C%22id%22%3A%22one%22%7D%2C%22two%22%3A%7B%22target%22%3A%22C3%27%27%22%2C%22id%22%3A%22two%22%7D%2C%22three%22%3A%7B%22target%22%3A%22C2%27%27%22%2C%22id%22%3A%22three%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%22%7D%2C%22C3%22%3A%7B%22parents%22%3A%5B%22C2%22%5D%2C%22id%22%3A%22C3%22%7D%2C%22C4%22%3A%7B%22parents%22%3A%5B%22C3%22%5D%2C%22id%22%3A%22C4%22%7D%2C%22C5%22%3A%7B%22parents%22%3A%5B%22C4%22%5D%2C%22id%22%3A%22C5%22%7D%2C%22C2%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%27%22%7D%2C%22C2%27%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C2%27%27%22%7D%2C%22C4%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C4%27%22%7D%2C%22C3%27%22%3A%7B%22parents%22%3A%5B%22C4%27%22%5D%2C%22id%22%3A%22C3%27%22%7D%2C%22C2%27%27%27%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C2%27%27%27%22%7D%2C%22C2%27%5E4%22%3A%7B%22parents%22%3A%5B%22C3%27%22%5D%2C%22id%22%3A%22C2%27%5E4%22%7D%2C%22C5%27%22%3A%7B%22parents%22%3A%5B%22C1%22%5D%2C%22id%22%3A%22C5%27%22%7D%2C%22C4%27%27%22%3A%7B%22parents%22%3A%5B%22C5%27%22%5D%2C%22id%22%3A%22C4%27%27%22%7D%2C%22C3%27%27%22%3A%7B%22parents%22%3A%5B%22C4%27%27%22%5D%2C%22id%22%3A%22C3%27%27%22%7D%7D%2C%22HEAD%22%3A%7B%22target%22%3A%22two%22%2C%22id%22%3A%22HEAD%22%7D%7D': false,
// done with level, tons of extra amends
'%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C5%22%2C%22id%22%3A%22main%22%7D%2C%22one%22%3A%7B%22target%22%3A%22C2%27%5E4%22%2C%22id%22%3A%22one%22%7D%2C%22two%22%3A%7B%22target%22%3A%22C2%27%5E5%22%2C%22id%22%3A%22two%22%7D%2C%22three%22%3A%7B%22target%22%3A%22C2%27%27%22%2C%22id%22%3A%22three%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22parents%22%3A%5B%5D%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22parents%22%3A%5B%22C0%22%5D%2C%22id%22%3A%22C1%22%7D%2C%22C2%22%3A%7B%22parents%22%3A%5B%22C
gitextract_ow2z9stj/ ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ ├── settings.yml │ └── workflows/ │ ├── build-docker.yml │ └── containerize.yml ├── .gitignore ├── .gitpod.yml ├── .jshintrc ├── .travis.yml ├── CLAUDE.md ├── CNAME ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── Makefile ├── README.md ├── __tests__/ │ ├── CommandLineStore.spec.js │ ├── GlobalStateStore.spec.js │ ├── LevelStore.spec.js │ ├── LocaleStore.spec.js │ ├── animation.spec.js │ ├── base.js │ ├── collections.spec.js │ ├── commandModel.spec.js │ ├── create.js │ ├── errors.spec.js │ ├── eventEmitter.spec.js │ ├── git.spec.js │ ├── levels.spec.js │ ├── mercurial.spec.js │ ├── remote.spec.js │ ├── simpleRemote.spec.js │ ├── treeCompare.spec.js │ ├── vcs.spec.js │ └── visuals.spec.js ├── checkgit.sh ├── generatedDocs/ │ ├── github-markdown.css │ └── levels.html ├── gulpfile.js ├── package.json ├── scripts/ │ ├── check-package-manager.js │ └── translate.js ├── src/ │ ├── js/ │ │ ├── actions/ │ │ │ ├── CommandLineActions.js │ │ │ ├── GlobalStateActions.js │ │ │ ├── LevelActions.js │ │ │ └── LocaleActions.js │ │ ├── app/ │ │ │ └── index.js │ │ ├── commands/ │ │ │ └── index.js │ │ ├── constants/ │ │ │ └── AppConstants.js │ │ ├── dialogs/ │ │ │ ├── confirmShowSolution.js │ │ │ ├── levelBuilder.js │ │ │ ├── nextLevel.js │ │ │ └── sandbox.js │ │ ├── dispatcher/ │ │ │ └── AppDispatcher.js │ │ ├── git/ │ │ │ ├── commands.js │ │ │ ├── gitShim.js │ │ │ ├── headless.js │ │ │ └── index.js │ │ ├── graph/ │ │ │ ├── index.js │ │ │ └── treeCompare.js │ │ ├── intl/ │ │ │ ├── checkStrings.js │ │ │ ├── index.js │ │ │ └── strings.js │ │ ├── level/ │ │ │ ├── builder.js │ │ │ ├── disabledMap.js │ │ │ ├── index.js │ │ │ └── parseWaterfall.js │ │ ├── log/ │ │ │ └── index.js │ │ ├── mercurial/ │ │ │ └── commands.js │ │ ├── models/ │ │ │ ├── collections.js │ │ │ └── commandModel.js │ │ ├── react_views/ │ │ │ ├── CommandHistoryView.jsx │ │ │ ├── CommandView.jsx │ │ │ ├── CommandsHelperBarView.jsx │ │ │ ├── HelperBarView.jsx │ │ │ ├── IntlHelperBarView.jsx │ │ │ ├── LevelToolbarView.jsx │ │ │ └── MainHelperBarView.jsx │ │ ├── sandbox/ │ │ │ ├── commands.js │ │ │ └── index.js │ │ ├── stores/ │ │ │ ├── CommandLineStore.js │ │ │ ├── GlobalStateStore.js │ │ │ ├── LevelStore.js │ │ │ └── LocaleStore.js │ │ ├── util/ │ │ │ ├── constants.js │ │ │ ├── debounce.js │ │ │ ├── debug.js │ │ │ ├── errors.js │ │ │ ├── escapeString.js │ │ │ ├── eventBaton.js │ │ │ ├── eventEmitter.js │ │ │ ├── index.js │ │ │ ├── keyMirror.js │ │ │ ├── keyboard.js │ │ │ ├── mock.js │ │ │ ├── promise.js │ │ │ ├── reactUtil.js │ │ │ ├── throttle.js │ │ │ └── zoomLevel.js │ │ ├── views/ │ │ │ ├── builderViews.js │ │ │ ├── commandViews.js │ │ │ ├── gitDemonstrationView.js │ │ │ ├── index.js │ │ │ ├── levelDropdownView.js │ │ │ ├── multiView.js │ │ │ └── rebaseView.js │ │ └── visuals/ │ │ ├── animation/ │ │ │ ├── animationFactory.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── tree.js │ │ ├── visBase.js │ │ ├── visBranch.js │ │ ├── visEdge.js │ │ ├── visNode.js │ │ ├── visTag.js │ │ └── visualization.js │ ├── levels/ │ │ ├── advanced/ │ │ │ └── multipleParents.js │ │ ├── index.js │ │ ├── intro/ │ │ │ ├── branching.js │ │ │ ├── commits.js │ │ │ ├── merging.js │ │ │ └── rebasing.js │ │ ├── mixed/ │ │ │ ├── describe.js │ │ │ ├── grabbingOneCommit.js │ │ │ ├── jugglingCommits.js │ │ │ ├── jugglingCommits2.js │ │ │ └── tags.js │ │ ├── rampup/ │ │ │ ├── cherryPick.js │ │ │ ├── detachedHead.js │ │ │ ├── interactiveRebase.js │ │ │ ├── relativeRefs.js │ │ │ ├── relativeRefs2.js │ │ │ └── reversingChanges.js │ │ ├── rebase/ │ │ │ ├── manyRebases.js │ │ │ └── selectiveRebase.js │ │ └── remote/ │ │ ├── clone.js │ │ ├── fakeTeamwork.js │ │ ├── fetch.js │ │ ├── fetchArgs.js │ │ ├── fetchRebase.js │ │ ├── lockedMain.js │ │ ├── mergeManyFeatures.js │ │ ├── pull.js │ │ ├── pullArgs.js │ │ ├── push.js │ │ ├── pushArgs.js │ │ ├── pushArgs2.js │ │ ├── pushManyFeatures.js │ │ ├── remoteBranches.js │ │ ├── sourceNothing.js │ │ └── tracking.js │ ├── style/ │ │ ├── font-awesome.css │ │ ├── main.css │ │ └── rainbows.css │ └── template.index.html └── vite.config.js
SYMBOL INDEX (850 symbols across 48 files)
FILE: __tests__/create.js
function getFile (line 7) | function getFile(truthy) {
function writeFile (line 14) | function writeFile(truthy, content) {
FILE: __tests__/eventEmitter.spec.js
function Sandbox (line 320) | function Sandbox() {
function Sandbox (line 342) | function Sandbox() {
function Level (line 365) | function Level() {
function Level (line 413) | function Level() {
function Level (line 446) | function Level() {
function Level (line 476) | function Level() {
function CanvasTerminalHolder (line 495) | function CanvasTerminalHolder(parent) {
FILE: scripts/translate.js
function translate (line 6) | async function translate(text, locale) {
function translateLevels (line 13) | async function translateLevels(locale) {
function translateStrings (line 53) | async function translateStrings(locale) {
function getAllLevelFiles (line 78) | function getAllLevelFiles(dir) {
function translateDialog (line 94) | async function translateDialog(dialog, locale) {
function getLevelTranslationStatus (line 108) | function getLevelTranslationStatus(locale, levelFiles) {
function getStringsTranslationStatus (line 138) | function getStringsTranslationStatus(locale) {
function listLocales (line 162) | function listLocales() {
function checkStatus (line 206) | function checkStatus(locale) {
function normalize (line 242) | function normalize() {
function main (line 277) | async function main() {
FILE: src/js/app/index.js
function tryLocaleDetect (line 295) | function tryLocaleDetect() {
function changeLocaleFromHeaders (line 300) | function changeLocaleFromHeaders(langString) {
function CommandUI (line 314) | function CommandUI() {
FILE: src/js/commands/index.js
function CommandOptionParser (line 173) | function CommandOptionParser(vcs, method, options) {
FILE: src/js/git/commands.js
function isColonRefspec (line 17) | function isColonRefspec(str) {
FILE: src/js/git/gitShim.js
function GitShim (line 6) | function GitShim(options) {
FILE: src/js/git/headless.js
function getMockFactory (line 18) | function getMockFactory() {
function getMockVisualization (line 43) | function getMockVisualization() {
FILE: src/js/git/index.js
function catchShortCircuit (line 19) | function catchShortCircuit(err) {
function GitEngine (line 25) | function GitEngine(options) {
class Ref (line 2895) | class Ref {
method constructor (line 2896) | constructor(options = {}) {
method get (line 2904) | get(key) { return this.attributes[key]; }
method set (line 2906) | set(key, value) {
method on (line 2925) | on(eventName, callback, context) {
method trigger (line 2930) | trigger(eventName, ...args) {
method toJSON (line 2935) | toJSON() { return Object.assign({}, this.attributes); }
method initialize (line 2937) | initialize() {
method getIsRemote (line 2954) | getIsRemote() {
method getName (line 2958) | getName() {
method targetChanged (line 2962) | targetChanged(model, targetValue, ev) {
method toString (line 2970) | toString() {
class Branch (line 2975) | class Branch extends Ref {
method constructor (line 2976) | constructor(options = {}) {
method initialize (line 2987) | initialize() {
method setRemoteTrackingBranchID (line 3001) | setRemoteTrackingBranchID(id) {
method getRemoteTrackingBranchID (line 3005) | getRemoteTrackingBranchID() {
method getPrefixedID (line 3009) | getPrefixedID() {
method getBaseID (line 3016) | getBaseID() {
method getIsRemote (line 3023) | getIsRemote() {
class Commit (line 3031) | class Commit {
method constructor (line 3032) | constructor(options = {}) {
method constants (line 3052) | static get constants() {
method id (line 3059) | get id() { return this.attributes.id; }
method get (line 3061) | get(key) { return this.attributes[key]; }
method set (line 3063) | set(key, value) {
method on (line 3072) | on(eventName, callback, context) {
method trigger (line 3077) | trigger(eventName, ...args) {
method toJSON (line 3082) | toJSON() { return Object.assign({}, this.attributes); }
method getLogEntry (line 3084) | getLogEntry() {
method getShowEntry (line 3095) | getShowEntry() {
method validateAtInit (line 3108) | validateAtInit() {
method addNodeToVisuals (line 3130) | addNodeToVisuals() {
method addEdgeToVisuals (line 3135) | addEdgeToVisuals(parent) {
method getParent (line 3139) | getParent(parentNum) {
method removeFromParents (line 3147) | removeFromParents() {
method checkForUpdatedParent (line 3153) | checkForUpdatedParent(engine) {
method removeChild (line 3189) | removeChild(childToRemove) {
method isMainParent (line 3199) | isMainParent(parent) {
method initialize (line 3204) | initialize(options) {
class Tag (line 3215) | class Tag extends Ref {
method constructor (line 3216) | constructor(options = {}) {
method initialize (line 3225) | initialize() {
function RevisionRange (line 3231) | function RevisionRange(engine, specifiers) {
FILE: src/js/graph/index.js
function invariant (line 1) | function invariant(truthy, reason) {
FILE: src/js/level/builder.js
class LevelBuilder (line 41) | class LevelBuilder extends Level {
method initialize (line 42) | initialize(options) {
method initName (line 77) | initName() {
method initGoalData (line 80) | initGoalData(options) {
method minimizeGoal (line 92) | minimizeGoal(position, size) {
method doBothVis (line 102) | doBothVis(method) {
method resizeGoal (line 111) | resizeGoal() {
method initStartVisualization (line 115) | initStartVisualization() {
method startOffCommand (line 133) | startOffCommand() {
method objectiveDialog (line 140) | objectiveDialog(command, deferred, levelObj) {
method initParseWaterfall (line 155) | initParseWaterfall(options) {
method buildLevel (line 168) | buildLevel(command, deferred) {
method getInstantCommands (line 176) | getInstantCommands() {
method takeControl (line 186) | takeControl() {
method releaseControl (line 192) | releaseControl() {
method showGoal (line 198) | showGoal() {
method showStart (line 203) | showStart(command, deferred) {
method resetSolution (line 208) | resetSolution() {
method hideStart (line 213) | hideStart(command, deferred) {
method defineStart (line 217) | defineStart(command, deferred) {
method defineGoal (line 229) | defineGoal(command, deferred) {
method defineName (line 248) | defineName(command, deferred) {
method defineHint (line 256) | defineHint(command, deferred) {
method editDialog (line 263) | editDialog(command, deferred) {
method finish (line 285) | finish(command, deferred) {
method getExportObj (line 359) | getExportObj() {
method processLevelBuilderCommand (line 372) | processLevelBuilderCommand(command, deferred) {
method afterCommandDefer (line 391) | afterCommandDefer(defer, command) {
method die (line 396) | die() {
FILE: src/js/level/disabledMap.js
function DisabledMap (line 8) | function DisabledMap(options) {
FILE: src/js/level/index.js
class Level (line 40) | class Level extends Sandbox {
method initialize (line 41) | initialize(options) {
method getIsGoalExpanded (line 69) | getIsGoalExpanded() {
method handleOpen (line 73) | handleOpen(deferred) {
method objectiveDialog (line 102) | objectiveDialog(command, deferred, levelObj) {
method startDialog (line 127) | startDialog(command, deferred) {
method getEnglishName (line 142) | getEnglishName() {
method initName (line 146) | initName() {
method initGoalData (line 163) | initGoalData(options) {
method takeControl (line 169) | takeControl() {
method releaseControl (line 175) | releaseControl() {
method startOffCommand (line 181) | startOffCommand() {
method initVisualization (line 199) | initVisualization(options) {
method initGoalVisualization (line 206) | initGoalVisualization() {
method minimizeGoal (line 251) | minimizeGoal(position, size) {
method resizeGoal (line 265) | resizeGoal() {
method showSolution (line 271) | showSolution(command, deferred) {
method toggleObjective (line 321) | toggleObjective() {
method toggleGoal (line 328) | toggleGoal() {
method showGoal (line 336) | showGoal(command, defer) {
method showSideVis (line 348) | showSideVis(command, defer, canvasHolder, initMethod) {
method hideGoal (line 360) | hideGoal(command, defer) {
method hideSideVis (line 366) | hideSideVis(command, defer, canvasHolder, vis) {
method initParseWaterfall (line 379) | initParseWaterfall(options) {
method initGitShim (line 405) | initGitShim(options) {
method undo (line 414) | undo() {
method beforeCommandCB (line 419) | beforeCommandCB(command) {
method afterCommandCB (line 427) | afterCommandCB(command) {
method doesCommandCountTowardsTotal (line 438) | doesCommandCountTowardsTotal(command) {
method afterCommandDefer (line 454) | afterCommandDefer(defer, command) {
method getNumSolutionCommands (line 473) | getNumSolutionCommands() {
method testOption (line 479) | testOption(option) {
method testOptionOnString (line 483) | testOptionOnString(str, option) {
method levelSolved (line 487) | levelSolved(defer) {
method die (line 584) | die() {
method getInstantCommands (line 611) | getInstantCommands() {
method reset (line 634) | reset(command, deferred) {
method buildLevel (line 648) | buildLevel(command, deferred) {
method importLevel (line 655) | importLevel(command, deferred) {
method startLevel (line 662) | startLevel(command, deferred) {
method exitLevel (line 671) | exitLevel(command, deferred) {
method processLevelCommand (line 686) | processLevelCommand(command, defer) {
FILE: src/js/models/collections.js
class BaseCollection (line 15) | class BaseCollection {
method constructor (line 16) | constructor(models) {
method on (line 28) | on(eventName, callback, context) {
method bind (line 36) | bind(eventName, callback, context) {
method off (line 40) | off(eventName, callback) {
method trigger (line 51) | trigger(eventName) {
method add (line 60) | add(model, options) {
method remove (line 73) | remove(model) {
method at (line 82) | at(index) {
method toArray (line 86) | toArray() {
method forEach (line 90) | forEach(callback, context) {
method each (line 95) | each(callback, context) {
method map (line 99) | map(callback, context) {
method filter (line 103) | filter(callback, context) {
method find (line 107) | find(callback, context) {
method reset (line 111) | reset(models) {
method toJSON (line 122) | toJSON() {
class CommitCollection (line 132) | class CommitCollection extends BaseCollection {
method constructor (line 133) | constructor(models) {
class CommandCollection (line 138) | class CommandCollection extends BaseCollection {
method constructor (line 139) | constructor(models) {
class BranchCollection (line 144) | class BranchCollection extends BaseCollection {
method constructor (line 145) | constructor(models) {
class TagCollection (line 150) | class TagCollection extends BaseCollection {
method constructor (line 151) | constructor(models) {
class CommandBuffer (line 156) | class CommandBuffer {
method constructor (line 157) | constructor(options) {
method on (line 167) | on(eventName, callback, context) {
method off (line 174) | off(eventName, callback) {
method trigger (line 185) | trigger(eventName) {
method get (line 194) | get(key) {
method set (line 198) | set(key, value) {
method addCommand (line 202) | addCommand(command) {
method touchBuffer (line 207) | touchBuffer() {
method setTimeout (line 218) | setTimeout() {
method popAndProcess (line 224) | popAndProcess() {
method processCommand (line 239) | processCommand(command) {
method clear (line 268) | clear() {
method sipFromBuffer (line 273) | sipFromBuffer() {
FILE: src/js/models/commandModel.js
class Command (line 17) | class Command {
method constructor (line 18) | constructor(options = {}) {
method initialize (line 48) | initialize() {
method on (line 62) | on(eventName, callback, context) {
method off (line 69) | off(eventName, callback) {
method trigger (line 80) | trigger(eventName) {
method get (line 89) | get(key) {
method set (line 93) | set(key, value) {
method toJSON (line 103) | toJSON() {
method destroy (line 107) | destroy() {
method initDefaults (line 115) | initDefaults() {
method replaceDotWithHead (line 123) | replaceDotWithHead(string) {
method appendOptionR (line 132) | appendOptionR() {
method prependOptionR (line 140) | prependOptionR() {
method mapDotToHead (line 147) | mapDotToHead() {
method deleteOptions (line 165) | deleteOptions(options) {
method getGeneralArgs (line 173) | getGeneralArgs() {
method setGeneralArgs (line 177) | setGeneralArgs(args) {
method setOptionsMap (line 181) | setOptionsMap(map) {
method getOptionsMap (line 185) | getOptionsMap() {
method acceptNoGeneralArgs (line 189) | acceptNoGeneralArgs() {
method argImpliedHead (line 197) | argImpliedHead(args, lower, upper, option) {
method oneArgImpliedHead (line 204) | oneArgImpliedHead(args, option) {
method twoArgsImpliedHead (line 208) | twoArgsImpliedHead(args, option) {
method threeArgsImpliedHead (line 212) | threeArgsImpliedHead(args, option) {
method oneArgImpliedOrigin (line 216) | oneArgImpliedOrigin(args) {
method twoArgsForOrigin (line 223) | twoArgsForOrigin(args) {
method impliedHead (line 227) | impliedHead(args, min) {
method validateArgBounds (line 234) | validateArgBounds(args, lower, upper, option) {
method validateAtInit (line 264) | validateAtInit() {
method setResult (line 273) | setResult(msg) {
method finishWith (line 277) | finishWith(deferred) {
method addWarning (line 282) | addWarning(msg) {
method parseOrCatch (line 289) | parseOrCatch() {
method errorChanged (line 316) | errorChanged() {
method formatError (line 330) | formatError() {
method expandShortcuts (line 334) | expandShortcuts(str) {
method processInstants (line 339) | processInstants() {
method parseAll (line 350) | parseAll() {
FILE: src/js/react_views/CommandHistoryView.jsx
class CommandHistoryView (line 14) | class CommandHistoryView extends React.Component {
method componentDidMount (line 16) | componentDidMount() {
method componentDidUpdate (line 30) | componentDidUpdate() {
method componentWillUnmount (line 35) | componentWillUnmount() {
method updateFromCollection (line 45) | updateFromCollection() {
method render (line 49) | render() {
method scrollDown (line 67) | scrollDown() {
method clearOldCommands (line 85) | clearOldCommands() {
FILE: src/js/react_views/CommandView.jsx
class CommandView (line 14) | class CommandView extends React.Component{
method componentDidMount (line 16) | componentDidMount() {
method componentWillUnmount (line 21) | componentWillUnmount() {
method updateStateFromModel (line 25) | updateStateFromModel() {
method constructor (line 35) | constructor(props, context) {
method render (line 45) | render() {
method renderResult (line 76) | renderResult() {
method renderFormattedWarnings (line 113) | renderFormattedWarnings() {
FILE: src/js/react_views/CommandsHelperBarView.jsx
class CommandsHelperBarView (line 9) | class CommandsHelperBarView extends React.Component {
method render (line 11) | render() {
method fireCommand (line 20) | fireCommand(command) {
method getItems (line 25) | getItems() {
FILE: src/js/react_views/HelperBarView.jsx
class HelperBarView (line 6) | class HelperBarView extends React.Component {
method render (line 8) | render() {
method renderItem (line 32) | renderItem(item, index) {
FILE: src/js/react_views/IntlHelperBarView.jsx
class IntlHelperBarView (line 9) | class IntlHelperBarView extends React.Component{
method render (line 11) | render() {
method fireCommand (line 20) | fireCommand(command) {
method getItems (line 26) | getItems() {
FILE: src/js/react_views/LevelToolbarView.jsx
class LevelToolbarView (line 7) | class LevelToolbarView extends React.Component {
method constructor (line 9) | constructor(props, context) {
method componentWillUnmount (line 17) | componentWillUnmount() {
method componentDidMount (line 20) | componentDidMount() {
method render (line 37) | render() {
FILE: src/js/react_views/MainHelperBarView.jsx
class MainHelperBarView (line 17) | class MainHelperBarView extends React.Component {
method constructor (line 19) | constructor(props, context) {
method render (line 26) | render() {
method showSelf (line 46) | showSelf() {
method getItems (line 52) | getItems() {
FILE: src/js/sandbox/index.js
class Sandbox (line 24) | class Sandbox {
method constructor (line 27) | constructor(options) {
method initialize (line 45) | initialize(options) {
method getDefaultVisEl (line 57) | getDefaultVisEl() {
method getAnimationTime (line 61) | getAnimationTime() { return 700 * 1.5; }
method initVisualization (line 63) | initVisualization(options) {
method initUndoStack (line 69) | initUndoStack(options) {
method initCommandCollection (line 73) | initCommandCollection(options) {
method initParseWaterfall (line 79) | initParseWaterfall(options) {
method initGitShim (line 83) | initGitShim(options) {
method takeControl (line 90) | takeControl() {
method releaseControl (line 103) | releaseControl() {
method releaseGitShim (line 115) | releaseGitShim() {
method insertGitShim (line 121) | insertGitShim() {
method beforeCommandCB (line 131) | beforeCommandCB(command) {
method afterCommandCB (line 135) | afterCommandCB(command) {
method pushUndo (line 139) | pushUndo() {
method undo (line 149) | undo(command, deferred) {
method commandSubmitted (line 165) | commandSubmitted(value) {
method startLevel (line 177) | startLevel(command, deferred) {
method buildLevel (line 217) | buildLevel(command, deferred) {
method exitLevel (line 236) | exitLevel(command, deferred) {
method showLevels (line 244) | showLevels(command, deferred) {
method sharePermalink (line 252) | sharePermalink(command, deferred) {
method resetSolved (line 262) | resetSolved(command, deferred) {
method processSandboxCommand (line 279) | processSandboxCommand(command, deferred) {
method hide (line 310) | hide() {
method levelExited (line 314) | levelExited() {
method show (line 318) | show() {
method importLevelNow (line 322) | importLevelNow(command, deferred) {
method importTreeNow (line 355) | importTreeNow(command, deferred) {
method importTree (line 374) | importTree(command, deferred) {
method importLevel (line 407) | importLevel(command, deferred) {
method exportTree (line 453) | exportTree(command, deferred) {
method clear (line 472) | clear(command, deferred) {
method mobileAlert (line 479) | mobileAlert(command, deferred) {
method delay (line 484) | delay(command, deferred) {
method reset (line 491) | reset(command, deferred) {
method helpDialog (line 500) | helpDialog(command, deferred) {
FILE: src/js/stores/CommandLineStore.js
function _checkForSize (line 20) | function _checkForSize() {
function _saveToLocalStorage (line 29) | function _saveToLocalStorage() {
FILE: src/js/stores/LevelStore.js
method length (line 31) | get length() {
function _syncToStorage (line 51) | function _syncToStorage() {
function getAliasMap (line 59) | function getAliasMap() {
function addToAliasMap (line 67) | function addToAliasMap(alias, expansion) {
function removeFromAliasMap (line 73) | function removeFromAliasMap(alias) {
FILE: src/js/stores/LocaleStore.js
function _getLocaleFromHeader (line 50) | function _getLocaleFromHeader(langString) {
FILE: src/js/util/errors.js
class MyError (line 3) | class MyError {
method constructor (line 4) | constructor(options = {}) {
method get (line 9) | get(key) {
method set (line 13) | set(key, value) {
method toString (line 17) | toString() {
method getMsg (line 21) | getMsg() {
class CommandProcessError (line 30) | class CommandProcessError extends MyError {
method constructor (line 31) | constructor(options = {}) {
class CommandResult (line 37) | class CommandResult extends MyError {
method constructor (line 38) | constructor(options = {}) {
class Warning (line 44) | class Warning extends MyError {
method constructor (line 45) | constructor(options = {}) {
class GitError (line 51) | class GitError extends MyError {
method constructor (line 52) | constructor(options = {}) {
FILE: src/js/util/eventBaton.js
function EventBaton (line 1) | function EventBaton(options) {
FILE: src/js/util/eventEmitter.js
class EventEmitter (line 4) | class EventEmitter {
method constructor (line 5) | constructor() {
method on (line 10) | on(eventName, callback, context) {
method bind (line 27) | bind(eventName, callback, context) {
method off (line 31) | off(eventName, callback, context) {
method unbind (line 59) | unbind(eventName, callback, context) {
method trigger (line 63) | trigger(eventName) {
method once (line 80) | once(eventName, callback, context) {
method listenTo (line 89) | listenTo(obj, eventName, callback) {
method stopListening (line 96) | stopListening(obj, eventName, callback) {
function createEvents (line 110) | function createEvents() {
function mixinEvents (line 115) | function mixinEvents(obj) {
FILE: src/js/util/keyboard.js
function KeyboardListener (line 16) | function KeyboardListener(options) {
FILE: src/js/util/promise.js
function createDeferred (line 1) | function createDeferred() {
function delay (line 11) | function delay(ms) {
FILE: src/js/util/zoomLevel.js
function detectZoom (line 2) | function detectZoom() {
FILE: src/js/views/builderViews.js
class TextGrabber (line 10) | class TextGrabber extends ContainedBase {
method constructor (line 11) | constructor(options) {
method getText (line 35) | getText() {
method setText (line 39) | setText(str) {
class MarkdownGrabber (line 44) | class MarkdownGrabber extends ContainedBase {
method constructor (line 45) | constructor(options) {
method confirmed (line 91) | confirmed() {
method cancelled (line 96) | cancelled() {
method keyup (line 101) | keyup() {
method getRawText (line 111) | getRawText() {
method exportToArray (line 115) | exportToArray() {
method getExportObj (line 119) | getExportObj() {
method updatePreview (line 125) | updatePreview() {
class MarkdownPresenter (line 132) | class MarkdownPresenter extends ContainedBase {
method constructor (line 133) | constructor(options) {
method grabText (line 168) | grabText() {
class DemonstrationBuilder (line 173) | class DemonstrationBuilder extends ContainedBase {
method constructor (line 174) | constructor(options) {
method testView (line 242) | testView() {
method getExportObj (line 252) | getExportObj() {
method confirmed (line 261) | confirmed() {
method cancelled (line 266) | cancelled() {
method getInsideElement (line 271) | getInsideElement() {
class MultiViewBuilder (line 276) | class MultiViewBuilder extends ContainedBase {
method constructor (line 277) | constructor(options) {
method saveView (line 314) | saveView() {
method cancel (line 319) | cancel() {
method addView (line 324) | addView(ev) {
method testOneView (line 346) | testOneView(ev) {
method testEntireView (line 356) | testEntireView() {
method editOneView (line 363) | editOneView(ev) {
method deleteOneView (line 386) | deleteOneView(ev) {
method addChildViewObj (line 396) | addChildViewObj(newObj, index) {
method setChildViews (line 403) | setChildViews(newArray) {
method getChildViews (line 407) | getChildViews() {
method update (line 411) | update() {
FILE: src/js/views/commandViews.js
class CommandPromptView (line 38) | class CommandPromptView {
method constructor (line 39) | constructor(options) {
method $ (line 48) | $(selector) {
method initialize (line 52) | initialize() {
method blur (line 70) | blur() {
method focus (line 74) | focus() {
method hideCursor (line 79) | hideCursor() {
method showCursor (line 83) | showCursor() {
method toggleCursor (line 87) | toggleCursor(state) {
method onKeyDown (line 91) | onKeyDown(e) {
method onKeyUp (line 151) | onKeyUp(e) {
method badHtmlEncode (line 175) | badHtmlEncode(text) {
method updatePrompt (line 183) | updatePrompt(el) {
method commandSelectChange (line 227) | commandSelectChange(delta) {
method setTextField (line 243) | setTextField(value) {
method clear (line 247) | clear() {
method submit (line 251) | submit() {
method rollupCommands (line 259) | rollupCommands(numBack) {
method addToCommandHistory (line 271) | addToCommandHistory(value) {
method submitCommand (line 287) | submitCommand(value) {
FILE: src/js/views/gitDemonstrationView.js
class GitDemonstrationView (line 18) | class GitDemonstrationView extends ContainedBase {
method constructor (line 19) | constructor(options) {
method exit (line 91) | exit() {
method receiveMetaNav (line 104) | receiveMetaNav(navView, metaContainerView) {
method checkScroll (line 110) | checkScroll() {
method dispatchBeforeCommand (line 119) | dispatchBeforeCommand() {
method takeControl (line 132) | takeControl() {
method releaseControl (line 139) | releaseControl() {
method reset (line 147) | reset() {
method positive (line 155) | positive() {
method onResetButtonClick (line 166) | onResetButtonClick() {
method demonstrate (line 171) | demonstrate() {
method negative (line 183) | negative(e) {
method dispatchCommand (line 190) | dispatchCommand(value, whenDone) {
method tearDown (line 216) | tearDown() {
method hide (line 223) | hide() {
method show (line 235) | show() {
method die (line 250) | die() {
method initVis (line 254) | initVis() {
FILE: src/js/views/index.js
class BaseView (line 14) | class BaseView {
method constructor (line 15) | constructor(options) {
method $ (line 27) | $(selector) {
method getDestination (line 31) | getDestination() {
method tearDown (line 35) | tearDown() {
method renderAgain (line 42) | renderAgain(HTML) {
method render (line 47) | render(HTML) {
class ResolveRejectBase (line 54) | class ResolveRejectBase extends BaseView {
method resolve (line 55) | resolve() {
method reject (line 59) | reject() {
class PositiveNegativeBase (line 64) | class PositiveNegativeBase extends BaseView {
method positive (line 65) | positive() {
method exit (line 69) | exit() {
method negative (line 73) | negative() {
class ContainedBase (line 78) | class ContainedBase extends BaseView {
method getAnimationTime (line 79) | getAnimationTime() { return 700; }
method show (line 81) | show() {
method hide (line 85) | hide() {
method die (line 89) | die() {
class GeneralButton (line 97) | class GeneralButton extends ContainedBase {
method constructor (line 98) | constructor(options) {
method click (line 124) | click() {
method sendClick (line 134) | sendClick() {
class ConfirmCancelView (line 139) | class ConfirmCancelView extends ResolveRejectBase {
method constructor (line 140) | constructor(options) {
class LeftRightView (line 165) | class LeftRightView extends PositiveNegativeBase {
method constructor (line 166) | constructor(options) {
method exit (line 192) | exit() {
method positive (line 197) | positive() {
method negative (line 202) | negative() {
class ModalView (line 208) | class ModalView {
method constructor (line 209) | constructor(options) {
method $ (line 222) | $(selector) {
method getAnimationTime (line 226) | getAnimationTime() { return 700; }
method render (line 228) | render() {
method stealKeyboard (line 233) | stealKeyboard() {
method releaseKeyboard (line 241) | releaseKeyboard() {
method onWindowFocus (line 249) | onWindowFocus(e) {}
method onDocumentClick (line 250) | onDocumentClick(e) {}
method onKeyDown (line 251) | onKeyDown(e) { e.preventDefault(); }
method onKeyUp (line 252) | onKeyUp(e) { e.preventDefault(); }
method show (line 254) | show() {
method hide (line 261) | hide() {
method getInsideElement (line 270) | getInsideElement() {
method toggleShow (line 274) | toggleShow(value) {
method toggleZ (line 295) | toggleZ(value) {
method tearDown (line 299) | tearDown() {
class ModalTerminal (line 305) | class ModalTerminal extends ContainedBase {
method constructor (line 306) | constructor(options) {
method updateTitle (line 324) | updateTitle(title) {
method onCloseButtonClick (line 328) | onCloseButtonClick() {
method onClick (line 332) | onClick() {
method getInsideElement (line 336) | getInsideElement() {
class ModalAlert (line 341) | class ModalAlert extends ContainedBase {
method constructor (line 342) | constructor(options) {
method render (line 367) | render() {
method getDestination (line 377) | getDestination() {
class ConfirmCancelTerminal (line 382) | class ConfirmCancelTerminal {
method constructor (line 383) | constructor(options) {
method positive (line 423) | positive() {
method negative (line 427) | negative() {
method getAnimationTime (line 431) | getAnimationTime() { return 700; }
method show (line 433) | show() {
method hide (line 437) | hide() {
method getPromise (line 441) | getPromise() {
method close (line 445) | close() {
class NextLevelConfirm (line 451) | class NextLevelConfirm extends ConfirmCancelTerminal {
method constructor (line 452) | constructor(options) {
class ViewportAlert (line 496) | class ViewportAlert {
method constructor (line 497) | constructor(options) {
method grabBatons (line 505) | grabBatons() {
method releaseBatons (line 509) | releaseBatons() {
method finish (line 513) | finish() {
class WindowSizeAlertWindow (line 519) | class WindowSizeAlertWindow extends ViewportAlert {
method constructor (line 520) | constructor(options) {
method eventBatonName (line 527) | get eventBatonName() { return 'windowSizeCheck'; }
method markdowns (line 528) | get markdowns() {
method batonFired (line 537) | batonFired(size) {
class ZoomAlertWindow (line 545) | class ZoomAlertWindow extends ViewportAlert {
method constructor (line 546) | constructor(options) {
method eventBatonName (line 552) | get eventBatonName() { return 'zoomChange'; }
method markdowns (line 553) | get markdowns() {
method batonFired (line 562) | batonFired(level) {
class CanvasTerminalHolder (line 570) | class CanvasTerminalHolder extends BaseView {
method constructor (line 571) | constructor(options) {
method getAnimationTime (line 607) | getAnimationTime() { return 700; }
method onClick (line 609) | onClick() {
method die (line 613) | die() {
method minimize (line 622) | minimize() {
method restore (line 637) | restore(pos, size) {
method recalcLayout (line 658) | recalcLayout() {
method getCanvasLocation (line 692) | getCanvasLocation() {
FILE: src/js/views/levelDropdownView.js
class LevelDropdownView (line 19) | class LevelDropdownView extends ContainedBase {
method constructor (line 20) | constructor(options) {
method render (line 93) | render() {
method onTabClick (line 105) | onTabClick(ev) {
method updateTabTo (line 115) | updateTabTo(id) {
method updateTabNames (line 125) | updateTabNames(names) {
method positive (line 131) | positive() {
method left (line 138) | left() {
method updateSelectedIcon (line 145) | updateSelectedIcon() {
method leftOrRight (line 150) | leftOrRight(delta) {
method right (line 173) | right() {
method up (line 180) | up() {
method down (line 188) | down() {
method downOrUp (line 196) | downOrUp() {
method turnOnKeyboardSelection (line 202) | turnOnKeyboardSelection() {
method turnOffKeyboardSelection (line 210) | turnOffKeyboardSelection() {
method getTabIndex (line 218) | getTabIndex() {
method switchToTabIndex (line 225) | switchToTabIndex(index) {
method wrapIndex (line 230) | wrapIndex(index, arr) {
method boundIndex (line 236) | boundIndex(index, arr) {
method getSequencesOnTab (line 242) | getSequencesOnTab() {
method getNextSequence (line 249) | getNextSequence() {
method getPreviousSequence (line 255) | getPreviousSequence() {
method getSequenceIndex (line 261) | getSequenceIndex(name) {
method getIndexForID (line 267) | getIndexForID(id) {
method selectFirst (line 271) | selectFirst() {
method getCurrentSequence (line 278) | getCurrentSequence() {
method getSelectedID (line 282) | getSelectedID() {
method selectIconByID (line 286) | selectIconByID(id) {
method deselectIconByID (line 290) | deselectIconByID(id) {
method toggleIconSelect (line 294) | toggleIconSelect(id, value) {
method negative (line 308) | negative() {
method testOption (line 312) | testOption(str) {
method show (line 316) | show(deferred, command) {
method hide (line 326) | hide() {
method loadLevelID (line 337) | loadLevelID(id) {
method updateSolvedStatus (line 350) | updateSolvedStatus() {
method buildSequences (line 356) | buildSequences() {
class SeriesView (line 368) | class SeriesView extends BaseView {
method constructor (line 369) | constructor(options) {
method updateSolvedStatus (line 414) | updateSolvedStatus() {
method getEventID (line 427) | getEventID(ev) {
method setAbout (line 432) | setAbout(content) {
method enterIcon (line 436) | enterIcon(ev) {
method updateAboutForLevelID (line 441) | updateAboutForLevelID(id) {
method formatLevelAbout (line 445) | formatLevelAbout(id) {
method getLevelNumberFromID (line 452) | getLevelNumberFromID(id) {
method click (line 457) | click(ev) {
FILE: src/js/views/multiView.js
class MultiView (line 14) | class MultiView {
method constructor (line 15) | constructor(options) {
method $ (line 77) | $(selector) {
method onWindowFocus (line 81) | onWindowFocus() {
method getAnimationTime (line 86) | getAnimationTime() {
method getPromise (line 90) | getPromise() {
method getPosFunc (line 94) | getPosFunc() {
method getNegFunc (line 100) | getNegFunc() {
method lock (line 106) | lock() {
method unlock (line 110) | unlock() {
method navForward (line 114) | navForward() {
method navBackward (line 126) | navBackward() {
method navIndexChange (line 134) | navIndexChange(delta) {
method hideViewIndex (line 140) | hideViewIndex(index) {
method showViewIndex (line 144) | showViewIndex(index) {
method finish (line 148) | finish() {
method start (line 160) | start() {
method createChildView (line 165) | createChildView(viewJSON) {
method addNavToView (line 178) | addNavToView(view, index) {
method render (line 192) | render() {
FILE: src/js/views/rebaseView.js
class InteractiveRebaseView (line 22) | class InteractiveRebaseView extends ContainedBase {
method constructor (line 23) | constructor(options) {
method restoreVis (line 57) | restoreVis() {
method confirm (line 61) | confirm() {
method render (line 84) | render() {
method cancel (line 111) | cancel() {
method makeButtons (line 119) | makeButtons() {
class RebaseEntry (line 137) | class RebaseEntry {
method constructor (line 138) | constructor(options = {}) {
method get (line 146) | get(key) {
method set (line 150) | set(key, value) {
method on (line 159) | on(eventName, callback, context) {
method trigger (line 166) | trigger(eventName) {
method toggle (line 175) | toggle() {
method toJSON (line 179) | toJSON() {
class RebaseEntryCollection (line 184) | class RebaseEntryCollection {
method constructor (line 185) | constructor() {
method add (line 191) | add(model) {
method each (line 197) | each(callback, context) {
method on (line 201) | on(eventName, callback, context) {
method trigger (line 208) | trigger(eventName) {
class RebaseEntryView (line 218) | class RebaseEntryView {
method constructor (line 219) | constructor(options) {
method $ (line 229) | $(selector) {
method toggle (line 233) | toggle() {
method render (line 238) | render() {
FILE: src/js/visuals/animation/index.js
class Animation (line 10) | class Animation {
method constructor (line 11) | constructor(options = {}) {
method get (line 17) | get(key) {
method set (line 21) | set(key, value) {
method on (line 30) | on(eventName, callback, context) {
method trigger (line 35) | trigger(eventName, ...args) {
method validateAtInit (line 40) | validateAtInit() {
method run (line 46) | run() {
class AnimationQueue (line 59) | class AnimationQueue {
method constructor (line 60) | constructor(options = {}) {
method get (line 69) | get(key) {
method set (line 73) | set(key, value) {
method on (line 82) | on(eventName, callback, context) {
method trigger (line 87) | trigger(eventName, ...args) {
method thenFinish (line 92) | thenFinish(promise, deferred) {
method add (line 106) | add(animation) {
method start (line 114) | start() {
method finish (line 122) | finish() {
method next (line 128) | next() {
class PromiseAnimation (line 160) | class PromiseAnimation {
method constructor (line 161) | constructor(options = {}) {
method get (line 173) | get(key) {
method set (line 177) | set(key, value) {
method on (line 186) | on(eventName, callback, context) {
method trigger (line 191) | trigger(eventName, ...args) {
method getPromise (line 196) | getPromise() {
method play (line 200) | play() {
method then (line 209) | then(func) {
method fromAnimation (line 213) | static fromAnimation(animation) {
FILE: src/js/visuals/index.js
function GitVisuals (line 16) | function GitVisuals(options) {
function blendHueStrings (line 959) | function blendHueStrings(hueStrings) {
FILE: src/js/visuals/tree.js
class VisBase (line 1) | class VisBase {
method constructor (line 2) | constructor(options = {}) {
method get (line 7) | get(key) {
method set (line 11) | set(key, value) {
method on (line 20) | on(eventName, callback, context) {
method trigger (line 25) | trigger(eventName, ...args) {
method removeKeys (line 30) | removeKeys(keys) {
method animateAttrKeys (line 38) | animateAttrKeys(keys, attrObj, speed, easing) {
FILE: src/js/visuals/visBase.js
class VisBase (line 3) | class VisBase {
method constructor (line 4) | constructor(options = {}) {
method get (line 9) | get(key) {
method set (line 13) | set(key, value) {
method on (line 24) | on(eventName, callback, context) {
method off (line 31) | off(eventName, callback) {
method trigger (line 42) | trigger(eventName) {
method toJSON (line 51) | toJSON() {
method removeKeys (line 55) | removeKeys(keys) {
method getNonAnimateKeys (line 63) | getNonAnimateKeys() {
method getIsInOrigin (line 69) | getIsInOrigin() {
method animateToAttr (line 76) | animateToAttr(attr, speed, easing) {
method setAttrBase (line 87) | setAttrBase(keys, attr, instant, speed, easing) {
method animateAttrKeys (line 108) | animateAttrKeys(keys, attrObj, speed, easing) {
FILE: src/js/visuals/visBranch.js
constant BRANCH_COLOR_PALETTE (line 8) | const BRANCH_COLOR_PALETTE = [
constant BRANCH_NAME_COLOR_MAP (line 40) | const BRANCH_NAME_COLOR_MAP = {
function getBranchColor (line 54) | function getBranchColor(branchName) {
class VisBranch (line 73) | class VisBranch extends VisBase {
method constructor (line 74) | constructor(options) {
method validateAtInit (line 105) | validateAtInit() {
method getID (line 111) | getID() {
method initialize (line 115) | initialize() {
method getCommitPosition (line 141) | getCommitPosition() {
method getDashArray (line 150) | getDashArray() {
method getIsGoalAndNotCompared (line 157) | getIsGoalAndNotCompared() {
method getIsLevelBranchCompared (line 169) | getIsLevelBranchCompared() {
method getIsMain (line 178) | getIsMain() {
method getFlipValue (line 182) | getFlipValue(commit, visNode) {
method refreshOffset (line 204) | refreshOffset() {
method getArrowTransform (line 217) | getArrowTransform() {
method getBranchStackIndex (line 225) | getBranchStackIndex() {
method getBranchStackLength (line 241) | getBranchStackLength() {
method isBranchStackEmpty (line 250) | isBranchStackEmpty() {
method getCommitID (line 258) | getCommitID() {
method getBranchStackArray (line 267) | getBranchStackArray() {
method getTextPosition (line 278) | getTextPosition() {
method getRectPosition (line 290) | getRectPosition() {
method getArrowPath (line 302) | getArrowPath() {
method getTextSize (line 352) | getTextSize() {
method getSingleRectSize (line 387) | getSingleRectSize() {
method getRectSize (line 397) | getRectSize() {
method getIsRemote (line 411) | getIsRemote() {
method getName (line 415) | getName() {
method nonTextToFront (line 429) | nonTextToFront() {
method textToFront (line 434) | textToFront() {
method textToFrontIfInStack (line 438) | textToFrontIfInStack() {
method getFill (line 444) | getFill() {
method remove (line 459) | remove() {
method handleModeChange (line 465) | handleModeChange() {
method genGraphics (line 469) | genGraphics(paper) {
method attachClickHandlers (line 507) | attachClickHandlers() {
method shouldDisableClick (line 522) | shouldDisableClick() {
method onClick (line 526) | onClick() {
method updateName (line 536) | updateName() {
method getNonTextOpacity (line 542) | getNonTextOpacity() {
method getTextOpacity (line 553) | getTextOpacity() {
method getStrokeWidth (line 565) | getStrokeWidth() {
method getAttributes (line 573) | getAttributes() {
method animateUpdatedPos (line 619) | animateUpdatedPos(speed, easing) {
method animateFromAttrToAttr (line 624) | animateFromAttrToAttr(fromAttr, toAttr, speed, easing) {
method setAttr (line 630) | setAttr(attr, instant, speed, easing) {
class VisBranchCollection (line 636) | class VisBranchCollection {
method constructor (line 637) | constructor() {
method add (line 642) | add(model) {
method remove (line 646) | remove(model) {
method reset (line 653) | reset() {
method each (line 657) | each(callback, context) {
method toArray (line 660) | toArray() {
method on (line 663) | on(eventName, callback, context) {
method trigger (line 667) | trigger(eventName, ...args) {
FILE: src/js/visuals/visEdge.js
class VisEdge (line 6) | class VisEdge extends VisBase {
method constructor (line 7) | constructor(options) {
method validateAtInit (line 18) | validateAtInit() {
method getID (line 27) | getID() {
method initialize (line 31) | initialize() {
method remove (line 41) | remove() {
method genSmoothBezierPathString (line 46) | genSmoothBezierPathString(tail, head) {
method genSmoothBezierPathStringFromCoords (line 52) | genSmoothBezierPathStringFromCoords(tailPos, headPos) {
method getBezierCurve (line 105) | getBezierCurve() {
method getStrokeColor (line 109) | getStrokeColor() {
method setOpacity (line 113) | setOpacity(opacity) {
method genGraphics (line 119) | genGraphics(paper) {
method getOpacity (line 133) | getOpacity() {
method getAttributes (line 146) | getAttributes() {
method animateUpdatedPath (line 157) | animateUpdatedPath(speed, easing) {
method animateFromAttrToAttr (line 162) | animateFromAttrToAttr(fromAttr, toAttr, speed, easing) {
method animateToAttr (line 168) | animateToAttr(attr, speed, easing) {
class VisEdgeCollection (line 184) | class VisEdgeCollection {
method constructor (line 185) | constructor() {
method add (line 190) | add(model) {
method remove (line 194) | remove(model) {
method reset (line 201) | reset() {
method each (line 205) | each(callback, context) {
method toArray (line 208) | toArray() {
method on (line 211) | on(eventName, callback, context) {
method trigger (line 215) | trigger(eventName, ...args) {
FILE: src/js/visuals/visNode.js
class VisNode (line 4) | class VisNode extends VisBase {
method constructor (line 5) | constructor(options) {
method getID (line 26) | getID() {
method validateAtInit (line 30) | validateAtInit() {
method initialize (line 46) | initialize() {
method setDepth (line 53) | setDepth(depth) {
method setDepthBasedOn (line 57) | setDepthBasedOn(depthIncrement, offset) {
method getMaxWidthScaled (line 65) | getMaxWidthScaled() {
method toFront (line 77) | toFront() {
method getOpacity (line 82) | getOpacity() {
method getTextScreenCoords (line 97) | getTextScreenCoords() {
method getAttributes (line 101) | getAttributes() {
method animatePositionTo (line 127) | animatePositionTo(visNode, speed, easing) {
method highlightTo (line 135) | highlightTo(visObj, speed, easing) {
method animateUpdatedPosition (line 149) | animateUpdatedPosition(speed, easing) {
method animateFromAttrToAttr (line 154) | animateFromAttrToAttr(fromAttr, toAttr, speed, easing) {
method animateToSnapshot (line 159) | animateToSnapshot(snapShot, speed, easing) {
method setAttr (line 166) | setAttr(attr, instant, speed, easing) {
method animateToAttr (line 171) | animateToAttr(attr, speed, easing) {
method getScreenCoords (line 184) | getScreenCoords() {
method getRadius (line 189) | getRadius() {
method getParentScreenCoords (line 193) | getParentScreenCoords() {
method setBirthPosition (line 197) | setBirthPosition() {
method setBirthFromSnapshot (line 213) | setBirthFromSnapshot(beforeSnapshot) {
method setBirth (line 237) | setBirth() {
method setOutgoingEdgesOpacity (line 242) | setOutgoingEdgesOpacity(opacity) {
method animateOutgoingEdgesToAttr (line 248) | animateOutgoingEdgesToAttr(snapShot, speed, easing) {
method animateOutgoingEdges (line 255) | animateOutgoingEdges(speed, easing) {
method animateOutgoingEdgesFromSnapshot (line 261) | animateOutgoingEdgesFromSnapshot(snapshot, speed, easing) {
method setOutgoingEdgesBirthPosition (line 268) | setOutgoingEdgesBirthPosition(parentCoords) {
method parentInFront (line 280) | parentInFront() {
method getFontSize (line 284) | getFontSize(str) {
method getFill (line 294) | getFill() {
method attachClickHandlers (line 306) | attachClickHandlers() {
method setOpacity (line 320) | setOpacity(opacity) {
method remove (line 330) | remove() {
method removeAll (line 339) | removeAll() {
method removeAllEdges (line 344) | removeAllEdges() {
method getExplodeStepFunc (line 350) | getExplodeStepFunc(speed) {
method makeCircle (line 398) | makeCircle(paper) {
method makeText (line 407) | makeText(paper) {
method genGraphics (line 412) | genGraphics() {
FILE: src/js/visuals/visTag.js
class VisTag (line 12) | class VisTag extends VisBase {
method constructor (line 13) | constructor(options) {
method validateAtInit (line 37) | validateAtInit() {
method getID (line 43) | getID() {
method initialize (line 47) | initialize() {
method getCommitPosition (line 60) | getCommitPosition() {
method getDashArray (line 67) | getDashArray() {
method getIsGoalAndNotCompared (line 74) | getIsGoalAndNotCompared() {
method getIsLevelTagCompared (line 86) | getIsLevelTagCompared() {
method getTagStackIndex (line 92) | getTagStackIndex() {
method getTagStackLength (line 108) | getTagStackLength() {
method isTagStackEmpty (line 117) | isTagStackEmpty() {
method getCommitID (line 125) | getCommitID() {
method getTagStackArray (line 130) | getTagStackArray() {
method getTextPosition (line 141) | getTextPosition() {
method getRectPosition (line 154) | getRectPosition() {
method getTextSize (line 165) | getTextSize() {
method getSingleRectSize (line 192) | getSingleRectSize() {
method getRectSize (line 202) | getRectSize() {
method getIsRemote (line 216) | getIsRemote() {
method getName (line 220) | getName() {
method nonTextToFront (line 228) | nonTextToFront() {
method textToFront (line 232) | textToFront() {
method textToFrontIfInStack (line 236) | textToFrontIfInStack() {
method remove (line 242) | remove() {
method handleModeChange (line 248) | handleModeChange() {
method genGraphics (line 252) | genGraphics(paper) {
method attachClickHandlers (line 285) | attachClickHandlers() {
method shouldDisableClick (line 299) | shouldDisableClick() {
method onClick (line 303) | onClick() {
method updateName (line 313) | updateName() {
method getNonTextOpacity (line 319) | getNonTextOpacity() {
method getTextOpacity (line 330) | getTextOpacity() {
method getStrokeWidth (line 342) | getStrokeWidth() {
method getAttributes (line 350) | getAttributes() {
method animateUpdatedPos (line 386) | animateUpdatedPos(speed, easing) {
method animateFromAttrToAttr (line 391) | animateFromAttrToAttr(fromAttr, toAttr, speed, easing) {
method setAttr (line 397) | setAttr(attr, instant, speed, easing) {
class VisTagCollection (line 403) | class VisTagCollection {
method constructor (line 404) | constructor() {
method add (line 409) | add(model) {
method remove (line 413) | remove(model) {
method reset (line 420) | reset() {
method each (line 424) | each(callback, context) {
method toArray (line 427) | toArray() {
method on (line 430) | on(eventName, callback, context) {
method trigger (line 434) | trigger(eventName, ...args) {
FILE: src/js/visuals/visualization.js
function createEventEmitter (line 10) | function createEventEmitter() {
class Visualization (line 32) | class Visualization {
method constructor (line 33) | constructor(options = {}) {
method get (line 58) | get(key) {
method set (line 62) | set(key, value) {
method on (line 71) | on(eventName, callback, context) {
method trigger (line 76) | trigger(eventName, ...args) {
method paperInitialize (line 81) | paperInitialize(paper, options) {
method clearOrigin (line 147) | clearOrigin() {
method makeOrigin (line 151) | makeOrigin(options) {
method originToo (line 175) | originToo(methodToCall, args) {
method setTreeIndex (line 193) | setTreeIndex(level) {
method setTreeOpacity (line 198) | setTreeOpacity(level) {
method getAnimationTime (line 207) | getAnimationTime() { return 300; }
method fadeTreeIn (line 209) | fadeTreeIn() {
method fadeTreeOut (line 219) | fadeTreeOut() {
method hide (line 227) | hide() {
method show (line 236) | show() {
method showHarsh (line 243) | showHarsh() {
method resetFromThisTreeNow (line 250) | resetFromThisTreeNow(treeString) {
method getOriginInTreeString (line 260) | getOriginInTreeString(treeString) {
method reset (line 265) | reset(tree) {
method tearDown (line 286) | tearDown(options) {
method die (line 295) | die() {
method myResize (line 305) | myResize() {
Condensed preview — 156 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,640K chars).
[
{
"path": ".editorconfig",
"chars": 83,
"preview": "# Top-most editorconfig file\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 64,
"preview": "# These are supported funding model platforms\ngithub: [pcottle]\n"
},
{
"path": ".github/settings.yml",
"chars": 237,
"preview": "repository:\n description: An interactive git visualization and tutorial. Aspiring students of git can use this app to"
},
{
"path": ".github/workflows/build-docker.yml",
"chars": 2608,
"preview": "name: Docker - learnGitBranching image\r\n\r\non:\r\n workflow_dispatch:\r\n push:\r\n branches:\r\n - main # Trigger CI "
},
{
"path": ".github/workflows/containerize.yml",
"chars": 757,
"preview": "name: containerize\n\non:\n push:\n branches: [ main ]\n workflow_dispatch:\n\njobs:\n build:\n runs-on: ubuntu-latest\n\n"
},
{
"path": ".gitignore",
"chars": 766,
"preview": "# NPM / Yarn / Pnpm\nnpm-debug.log\nyarn-error.log\npnpm-lock.yaml\npackage-lock.json\nnode_modules/\n\n# Build artifacts and a"
},
{
"path": ".gitpod.yml",
"chars": 285,
"preview": "ports:\n- port: 8000\n onOpen: open-preview\ntasks:\n- init: yarn install\n command: >\n yarn gulp fastBuild &&\n print"
},
{
"path": ".jshintrc",
"chars": 724,
"preview": "{\n \"esversion\": 6,\n \"curly\": true,\n \"eqeqeq\": false,\n \"regexp\": false,\n \"nonew\": false,\n \"latedef\": false,\n \"fori"
},
{
"path": ".travis.yml",
"chars": 455,
"preview": "sudo: false\nlanguage: node_js\nnode_js:\n - \"10\"\n - \"8\"\nbefore_install:\n - curl -o- -L https://yarnpkg.com/install.sh |"
},
{
"path": "CLAUDE.md",
"chars": 6299,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "CNAME",
"chars": 25,
"preview": "learnGitBranching.js.org\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 942,
"preview": "## Welcome to the Learn Git Branching contributing guide!\n\nWe have a pretty relaxed environment in this project so there"
},
{
"path": "Dockerfile",
"chars": 428,
"preview": "FROM node:14.20.0-alpine3.16 as build\r\n\r\nRUN apk add git --no-cache\r\nWORKDIR \"/src\"\r\n\r\nCOPY . /src\r\nRUN yarn install && "
},
{
"path": "LICENSE.md",
"chars": 1077,
"preview": "## MIT License\n\nCopyright (c) 2012-2025 Peter Cottle\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "Makefile",
"chars": 1135,
"preview": "ifeq ($(OS),Windows_NT)\r\n\tSHELL := pwsh.exe\r\nelse\r\n\tSHELL := pwsh\r\nendif\r\n\r\n.SHELLFLAGS := -NoProfile -Command\r\n\r\nREGIST"
},
{
"path": "README.md",
"chars": 11952,
"preview": "# LearnGitBranching\n\n[](https://github.com/pcot"
},
{
"path": "__tests__/CommandLineStore.spec.js",
"chars": 1489,
"preview": "var CommandLineActions = require('../src/js/actions/CommandLineActions');\nvar CommandLineStore = require('../src/js/stor"
},
{
"path": "__tests__/GlobalStateStore.spec.js",
"chars": 840,
"preview": "var GlobalStateActions = require('../src/js/actions/GlobalStateActions');\nvar GlobalStateStore = require('../src/js/stor"
},
{
"path": "__tests__/LevelStore.spec.js",
"chars": 1628,
"preview": "var LevelActions = require('../src/js/actions/LevelActions');\nvar LevelStore = require('../src/js/stores/LevelStore');\n\n"
},
{
"path": "__tests__/LocaleStore.spec.js",
"chars": 1127,
"preview": "var LocaleActions = require('../src/js/actions/LocaleActions');\nvar LocaleStore = require('../src/js/stores/LocaleStore'"
},
{
"path": "__tests__/animation.spec.js",
"chars": 784,
"preview": "var AnimationModule = require('../src/js/visuals/animation/index');\nvar PromiseAnimation = AnimationModule.PromiseAnimat"
},
{
"path": "__tests__/base.js",
"chars": 2617,
"preview": "var HeadlessGit = require('../src/js/git/headless').HeadlessGit;\nvar TreeCompare = require('../src/js/graph/treeCompare."
},
{
"path": "__tests__/collections.spec.js",
"chars": 3043,
"preview": "var collections = require('../src/js/models/collections');\nvar Command = require('../src/js/models/commandModel').Comman"
},
{
"path": "__tests__/commandModel.spec.js",
"chars": 5822,
"preview": "var Command = require('../src/js/models/commandModel').Command;\n\ndescribe('Command Model', function() {\n describe('init"
},
{
"path": "__tests__/create.js",
"chars": 1332,
"preview": "var TreeCompare = require('../src/js/graph/treeCompare');\nvar HeadlessGit = require('../src/js/git/headless').HeadlessGi"
},
{
"path": "__tests__/errors.spec.js",
"chars": 3007,
"preview": "var errors = require('../src/js/util/errors');\n\ndescribe('Error Models', function() {\n describe('GitError', function() "
},
{
"path": "__tests__/eventEmitter.spec.js",
"chars": 16241,
"preview": "var eventEmitter = require('../src/js/util/eventEmitter');\n\ndescribe('EventEmitter', function() {\n describe('on() and t"
},
{
"path": "__tests__/git.spec.js",
"chars": 38964,
"preview": "var intl = require('../src/js/intl')\nvar base = require('./base');\nvar expectTreeAsync = base.expectTreeAsync;\nvar runCo"
},
{
"path": "__tests__/levels.spec.js",
"chars": 505,
"preview": "var base = require('./base');\n\ndescribe('GitEngine Levels', function() {\n var sequences = require('../src/levels/index'"
},
{
"path": "__tests__/mercurial.spec.js",
"chars": 5556,
"preview": "var base = require('./base');\nvar expectTreeAsync = base.expectTreeAsync;\n\ndescribe('Mercurial', function() {\n var asse"
},
{
"path": "__tests__/remote.spec.js",
"chars": 68294,
"preview": "var base = require('./base');\nvar intl = require('../src/js/intl');\nvar expectTreeAsync = base.expectTreeAsync;\nvar runC"
},
{
"path": "__tests__/simpleRemote.spec.js",
"chars": 726,
"preview": "var base = require('./base');\nvar expectTreeAsync = base.expectTreeAsync;\n\ndescribe('Git Remote simple', function() {\n "
},
{
"path": "__tests__/treeCompare.spec.js",
"chars": 30624,
"preview": "var TreeCompare = require('../src/js/graph/treeCompare');\n\nvar loadTree = function(treeString) {\n return TreeCompare.co"
},
{
"path": "__tests__/vcs.spec.js",
"chars": 1228,
"preview": "var Command = require('../src/js/models/commandModel').Command;\n\ndescribe('commands', function() {\n it('replaces . with"
},
{
"path": "__tests__/visuals.spec.js",
"chars": 6282,
"preview": "\nvar VisBranch = require('../src/js/visuals/visBranch');\nvar GitVisuals = require('../src/js/visuals').GitVisuals;\n\ndesc"
},
{
"path": "checkgit.sh",
"chars": 156,
"preview": "GIT_STATUS=$(git status --porcelain | wc -l )\nif [[ GIT_STATUS -ne 0 ]]; then\n echo \"${1:-Source files were modified}\"\n"
},
{
"path": "generatedDocs/github-markdown.css",
"chars": 30320,
"preview": ".markdown-body {\n --base-size-4: 0.25rem;\n --base-size-8: 0.5rem;\n --base-size-16: 1rem;\n --base-size-24: 1.5rem;\n "
},
{
"path": "generatedDocs/levels.html",
"chars": 103329,
"preview": "\n <!DOCTYPE html>\n <html>\n <head>\n <title>Learn Git Branching - Level Documentation</title>\n <style>."
},
{
"path": "gulpfile.js",
"chars": 8103,
"preview": "var { execSync } = require('child_process');\nvar {\n writeFileSync, readdirSync, readFileSync,\n existsSync, statSync, m"
},
{
"path": "package.json",
"chars": 1494,
"preview": "{\n \"name\": \"LearnGitBranching\",\n \"version\": \"0.8.0\",\n \"description\": \"An interactive git visualization to challenge a"
},
{
"path": "scripts/check-package-manager.js",
"chars": 172,
"preview": "if (process.env.npm_execpath.indexOf('yarn') === -1) {\n console.error('This project uses yarn, not npm!!! Please use ya"
},
{
"path": "scripts/translate.js",
"chars": 12218,
"preview": "\nconst fs = require('fs');\nconst path = require('path');\n\n// This is a placeholder for a real translation API call\nasync"
},
{
"path": "src/js/actions/CommandLineActions.js",
"chars": 388,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/actions/GlobalStateActions.js",
"chars": 842,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/actions/LevelActions.js",
"chars": 725,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/actions/LocaleActions.js",
"chars": 713,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/app/index.js",
"chars": 11398,
"preview": "var jQuery = require('jquery');\nvar EventEmitter = require('events').EventEmitter;\nvar React = require('react');\nvar Rea"
},
{
"path": "src/js/commands/index.js",
"chars": 5984,
"preview": "var intl = require('../intl');\n\nvar Errors = require('../util/errors');\nvar GitCommands = require('../git/commands');\nva"
},
{
"path": "src/js/constants/AppConstants.js",
"chars": 877,
"preview": "\"use strict\";\n\nvar keyMirror = require('../util/keyMirror');\n\nvar CHANGE_EVENT = 'change';\n\nmodule.exports = {\n\n CHANGE"
},
{
"path": "src/js/dialogs/confirmShowSolution.js",
"chars": 4192,
"preview": "exports.dialog = {\n 'en_US': [{\n type: 'ModalAlert',\n options: {\n markdowns: [\n '## Are you sure you "
},
{
"path": "src/js/dialogs/levelBuilder.js",
"chars": 16773,
"preview": "exports.dialog = {\n 'en_US': [{\n type: 'ModalAlert',\n options: {\n markdowns: [\n '## Welcome to the le"
},
{
"path": "src/js/dialogs/nextLevel.js",
"chars": 5338,
"preview": "exports.dialog = {\n 'en_US': [{\n type: 'ModalAlert',\n options: {\n markdowns: [\n '## Great Job!!!',\n "
},
{
"path": "src/js/dialogs/sandbox.js",
"chars": 36279,
"preview": "const { Branch } = require(\"../git\");\n\nexports.dialog = {\n 'en_US': [{\n type: 'ModalAlert',\n options: {\n mar"
},
{
"path": "src/js/dispatcher/AppDispatcher.js",
"chars": 516,
"preview": "'use strict';\n\nvar AppConstants = require('../constants/AppConstants');\nvar Dispatcher = require('flux').Dispatcher;\n\nva"
},
{
"path": "src/js/git/commands.js",
"chars": 31850,
"preview": "var escapeString = require('../util/escapeString');\nvar intl = require('../intl');\n\nvar Graph = require('../graph');\nvar"
},
{
"path": "src/js/git/gitShim.js",
"chars": 2823,
"preview": "var createDeferred = require('../util/promise').createDeferred;\n\nvar Main = require('../app');\nvar MultiView = require('"
},
{
"path": "src/js/git/headless.js",
"chars": 3847,
"preview": "var createDeferred = require('../util/promise').createDeferred;\n\nvar GitEngine = require('../git').GitEngine;\nvar Animat"
},
{
"path": "src/js/git/index.js",
"chars": 94026,
"preview": "\nvar intl = require('../intl');\n\nvar AnimationFactory = require('../visuals/animation/animationFactory').AnimationFactor"
},
{
"path": "src/js/graph/index.js",
"chars": 4687,
"preview": "function invariant(truthy, reason) {\n if (!truthy) {\n throw new Error(reason);\n }\n}\n\nvar Graph = {\n\n getOrMakeRecu"
},
{
"path": "src/js/graph/treeCompare.js",
"chars": 15081,
"preview": "var _ = require('underscore');\n\n// static class...\nvar TreeCompare = {};\n\nTreeCompare.dispatchFromLevel = function(level"
},
{
"path": "src/js/intl/checkStrings.js",
"chars": 880,
"preview": "var { join } = require('path');\nvar { readFileSync } = require('fs');\n\nvar util = require('../util');\nvar { strings } = "
},
{
"path": "src/js/intl/index.js",
"chars": 3302,
"preview": "var LocaleStore = require('../stores/LocaleStore');\n\nvar _ = require('underscore');\nvar strings = require('../intl/strin"
},
{
"path": "src/js/intl/strings.js",
"chars": 158398,
"preview": "exports.strings = {\n \"finish-dialog-finished\": {\n \"__desc__\": \"One of the lines in the next level dialog\",\n \"ja\":"
},
{
"path": "src/js/level/builder.js",
"chars": 11017,
"preview": "var createDeferred = require('../util/promise').createDeferred;\n\nvar util = require('../util');\nvar Main = require('../a"
},
{
"path": "src/js/level/disabledMap.js",
"chars": 1155,
"preview": "var intl = require('../intl');\n\nvar Commands = require('../commands');\n\nvar Errors = require('../util/errors');\nvar GitE"
},
{
"path": "src/js/level/index.js",
"chars": 19899,
"preview": "var createDeferred = require('../util/promise').createDeferred;\nvar React = require('react');\nvar ReactDOM = require('re"
},
{
"path": "src/js/level/parseWaterfall.js",
"chars": 3419,
"preview": "var GitCommands = require('../git/commands');\nvar Commands = require('../commands');\nvar SandboxCommands = require('../s"
},
{
"path": "src/js/log/index.js",
"chars": 829,
"preview": "\nvar log = function(category, action, label) {\n window.gtag = window.gtag || function() {};\n window.gtag('event', acti"
},
{
"path": "src/js/mercurial/commands.js",
"chars": 5664,
"preview": "var intl = require('../intl');\n\nvar GitCommands = require('../git/commands');\nvar Errors = require('../util/errors');\n\nv"
},
{
"path": "src/js/models/collections.js",
"chars": 6686,
"preview": "// Collections - converted from Backbone to plain ES6 classes\n\nvar createDeferred = require('../util/promise').createDef"
},
{
"path": "src/js/models/commandModel.js",
"chars": 9150,
"preview": "// Command model - converted from Backbone to plain ES6 class\n\nvar Errors = require('../util/errors');\n\nvar ParseWaterfa"
},
{
"path": "src/js/react_views/CommandHistoryView.jsx",
"chars": 2638,
"preview": "var React = require('react');\nvar PropTypes = require('prop-types');\n\nvar CommandView = require('../react_views/CommandV"
},
{
"path": "src/js/react_views/CommandView.jsx",
"chars": 3216,
"preview": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar PropTypes = require('prop-types');\n\nvar reactUtil"
},
{
"path": "src/js/react_views/CommandsHelperBarView.jsx",
"chars": 1757,
"preview": "var React = require('react');\nvar PropTypes = require('prop-types');\nvar HelperBarView = require('../react_views/HelperB"
},
{
"path": "src/js/react_views/HelperBarView.jsx",
"chars": 1698,
"preview": "var React = require('react');\nvar PropTypes = require('prop-types');\n\nvar reactUtil = require('../util/reactUtil');\n\ncla"
},
{
"path": "src/js/react_views/IntlHelperBarView.jsx",
"chars": 4423,
"preview": "var PropTypes = require('prop-types');\n\nvar HelperBarView = require('../react_views/HelperBarView.jsx');\nvar Main = requ"
},
{
"path": "src/js/react_views/LevelToolbarView.jsx",
"chars": 2292,
"preview": "var React = require('react');\nvar PropTypes = require('prop-types');\n\nvar intl = require('../intl');\nvar reactUtil = req"
},
{
"path": "src/js/react_views/MainHelperBarView.jsx",
"chars": 1757,
"preview": "var HelperBarView = require('../react_views/HelperBarView.jsx');\nvar IntlHelperBarView =\n require('../react_views/IntlH"
},
{
"path": "src/js/sandbox/commands.js",
"chars": 8614,
"preview": "var util = require('../util');\n\nvar constants = require('../util/constants');\nvar intl = require('../intl');\n\nvar Comman"
},
{
"path": "src/js/sandbox/index.js",
"chars": 14266,
"preview": "var createDeferred = require('../util/promise').createDeferred;\nvar { createEvents } = require('../util/eventEmitter');\n"
},
{
"path": "src/js/stores/CommandLineStore.js",
"chars": 1789,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/stores/GlobalStateStore.js",
"chars": 1763,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/stores/LevelStore.js",
"chars": 5662,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/stores/LocaleStore.js",
"chars": 2939,
"preview": "\"use strict\";\n\nvar AppConstants = require('../constants/AppConstants');\nvar AppDispatcher = require('../dispatcher/AppDi"
},
{
"path": "src/js/util/constants.js",
"chars": 965,
"preview": "/**\n * Constants....!!!\n */\nvar TIME = {\n betweenCommandsDelay: 400\n};\n\nvar VIEWPORT = {\n minZoom: 0.55,\n maxZoom: 1."
},
{
"path": "src/js/util/debounce.js",
"chars": 422,
"preview": "module.exports = function(func, time, immediate) {\n var timeout;\n return function() {\n var later = function()"
},
{
"path": "src/js/util/debug.js",
"chars": 2383,
"preview": "var toGlobalize = {\n App: require('../app/index.js'),\n Tree: require('../visuals/tree'),\n Visuals: require('../visual"
},
{
"path": "src/js/util/errors.js",
"chars": 1428,
"preview": "// Error models - converted from Backbone to plain classes\n\nclass MyError {\n constructor(options = {}) {\n this.type "
},
{
"path": "src/js/util/escapeString.js",
"chars": 251,
"preview": "var mapping = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '/': '/'\n};\n\nmodu"
},
{
"path": "src/js/util/eventBaton.js",
"chars": 3312,
"preview": "function EventBaton(options) {\n this.eventMap = {};\n this.options = options || {};\n}\n\n// this method steals the \"baton"
},
{
"path": "src/js/util/eventEmitter.js",
"chars": 3688,
"preview": "// EventEmitter - replacement for Backbone.Events\n// Provides on, off, trigger, once, and listenTo methods\n\nclass EventE"
},
{
"path": "src/js/util/index.js",
"chars": 1817,
"preview": "var { readdirSync, lstatSync } = require('fs');\nvar { join } = require('path');\n\nvar escapeString = require('../util/esc"
},
{
"path": "src/js/util/keyMirror.js",
"chars": 345,
"preview": "\"use strict\";\n\n/**\n * Our own flavor of keyMirror since I get some weird\n * obscure error when trying to import the reac"
},
{
"path": "src/js/util/keyboard.js",
"chars": 1635,
"preview": "var Main = require('../app');\n\nvar mapKeycodeToKey = function(keycode) {\n // HELP WANTED -- internationalize? Dvorak? I"
},
{
"path": "src/js/util/mock.js",
"chars": 176,
"preview": "exports.mock = function(Constructor) {\n var dummy = {};\n var stub = function() {};\n\n for (var key in Constructor.prot"
},
{
"path": "src/js/util/promise.js",
"chars": 422,
"preview": "function createDeferred() {\n var resolve;\n var reject;\n var promise = new Promise(function(innerResolve, innerReject)"
},
{
"path": "src/js/util/reactUtil.js",
"chars": 105,
"preview": "var joinClasses = function(classes) {\n return classes.join(' ');\n};\n\nexports.joinClasses = joinClasses;\n"
},
{
"path": "src/js/util/throttle.js",
"chars": 258,
"preview": "module.exports = function(func, time) {\n var wait = false;\n return function() {\n if (!wait) {\n func.ap"
},
{
"path": "src/js/util/zoomLevel.js",
"chars": 638,
"preview": "var _warnOnce = true;\nfunction detectZoom() {\n /**\n * Note: this method has only been tested on Chrome\n * but seems"
},
{
"path": "src/js/views/builderViews.js",
"chars": 10597,
"preview": "var _ = require('underscore');\nvar createDeferred = require('../util/promise').createDeferred;\nvar { marked } = require("
},
{
"path": "src/js/views/commandViews.js",
"chars": 8667,
"preview": "// CommandPromptView - converted from Backbone.View to ES6 class\n\nconst {getAllCommands} = require('../sandbox/commands'"
},
{
"path": "src/js/views/gitDemonstrationView.js",
"chars": 7453,
"preview": "var _ = require('underscore');\nvar createDeferred = require('../util/promise').createDeferred;\nvar delay = require('../u"
},
{
"path": "src/js/views/index.js",
"chars": 18084,
"preview": "var _ = require('underscore');\nvar createDeferred = require('../util/promise').createDeferred;\nvar { marked } = require("
},
{
"path": "src/js/views/levelDropdownView.js",
"chars": 11791,
"preview": "var _ = require('underscore');\nvar LocaleStore = require('../stores/LocaleStore');\n\nvar util = require('../util');\nvar d"
},
{
"path": "src/js/views/multiView.js",
"chars": 4916,
"preview": "var createDeferred = require('../util/promise').createDeferred;\n\nvar LeftRightView = require('../views').LeftRightView;\n"
},
{
"path": "src/js/views/rebaseView.js",
"chars": 5883,
"preview": "var GitError = require('../util/errors').GitError;\nvar _ = require('underscore');\nvar createDeferred = require('../util/"
},
{
"path": "src/js/visuals/animation/animationFactory.js",
"chars": 4925,
"preview": "var delay = require('../../util/promise').delay;\n\nvar Animation = require('./index').Animation;\nvar PromiseAnimation = r"
},
{
"path": "src/js/visuals/animation/index.js",
"chars": 5476,
"preview": "var createDeferred = require('../../util/promise').createDeferred;\nvar GlobalStateActions = require('../../actions/Globa"
},
{
"path": "src/js/visuals/index.js",
"chars": 25981,
"preview": "var createDeferred = require('../util/promise').createDeferred;\n\nvar intl = require('../intl');\nvar GRAPHICS = require('"
},
{
"path": "src/js/visuals/tree.js",
"chars": 1530,
"preview": "class VisBase {\n constructor(options = {}) {\n this._events = {};\n this.attributes = Object.assign({}, options);\n "
},
{
"path": "src/js/visuals/visBase.js",
"chars": 3308,
"preview": "// VisBase - converted from Backbone.Model to ES6 class\n\nclass VisBase {\n constructor(options = {}) {\n this._events "
},
{
"path": "src/js/visuals/visBranch.js",
"chars": 17376,
"preview": "var GRAPHICS = require('../util/constants').GRAPHICS;\n\nvar VisBase = require('../visuals/visBase').VisBase;\nvar TreeComp"
},
{
"path": "src/js/visuals/visEdge.js",
"chars": 5889,
"preview": "var GRAPHICS = require('../util/constants').GRAPHICS;\n\nvar VisBase = require('../visuals/visBase').VisBase;\nvar GlobalSt"
},
{
"path": "src/js/visuals/visNode.js",
"chars": 10627,
"preview": "var GRAPHICS = require('../util/constants').GRAPHICS;\nvar VisBase = require('../visuals/visBase').VisBase;\n\nclass VisNod"
},
{
"path": "src/js/visuals/visTag.js",
"chars": 10230,
"preview": "var GRAPHICS = require('../util/constants').GRAPHICS;\n\nvar VisBase = require('../visuals/visBase').VisBase;\nvar TreeComp"
},
{
"path": "src/js/visuals/visualization.js",
"chars": 9502,
"preview": "var Collections = require('../models/collections');\nvar CommitCollection = Collections.CommitCollection;\nvar BranchColle"
},
{
"path": "src/levels/advanced/multipleParents.js",
"chars": 83813,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C7\\\",\\\"id\\\":\\\"main\\\"},\\\"bugWork\\\":{\\\"target"
},
{
"path": "src/levels/index.js",
"chars": 19463,
"preview": "// Each level is part of a \"sequence;\" levels within\n// a sequence proceed in the order listed here\nexports.levelSequenc"
},
{
"path": "src/levels/intro/branching.js",
"chars": 82137,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C1\\\",\\\"id\\\":\\\"main\\\"},\\\"bugFix\\\":{\\\"target\\"
},
{
"path": "src/levels/intro/commits.js",
"chars": 51766,
"preview": "exports.level = {\n \"name\": {\n \"en_US\": \"Introduction to Git Commits\",\n \"fa\": \"معرفی به Commit در Git\",\n \"de_DE"
},
{
"path": "src/levels/intro/merging.js",
"chars": 85060,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C4\\\",\\\"id\\\":\\\"main\\\"},\\\"bugFix\\\":{\\\"target\\"
},
{
"path": "src/levels/intro/rebasing.js",
"chars": 69233,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%22%2C%22id%22%3A%22ma"
},
{
"path": "src/levels/mixed/describe.js",
"chars": 58156,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C2\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/mixed/grabbingOneCommit.js",
"chars": 41533,
"preview": "exports.level = {\n \"compareOnlyMainHashAgnosticWithAsserts\": true,\n \"goalAsserts\": {\n \"main\": [\n function (dat"
},
{
"path": "src/levels/mixed/jugglingCommits.js",
"chars": 48986,
"preview": "exports.level = {\n \"disabledMap\": {\n \"git cherry-pick\": true,\n \"git revert\": true\n },\n \"compareOnlyMainHashAgno"
},
{
"path": "src/levels/mixed/jugglingCommits2.js",
"chars": 46951,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%2"
},
{
"path": "src/levels/mixed/tags.js",
"chars": 58155,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C5\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/rampup/cherryPick.js",
"chars": 63111,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C7%27%22%2C%22id%22%3A%2"
},
{
"path": "src/levels/rampup/detachedHead.js",
"chars": 66885,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C2\\\",\\\"id\\\":\\\"main\\\"},\\\"bugFix\\\":{\\\"target\\"
},
{
"path": "src/levels/rampup/interactiveRebase.js",
"chars": 76343,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C4%27%22%2C%22id%22%3A%2"
},
{
"path": "src/levels/rampup/relativeRefs.js",
"chars": 75229,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C2\\\",\\\"id\\\":\\\"main\\\"},\\\"bugFix\\\":{\\\"target\\"
},
{
"path": "src/levels/rampup/relativeRefs2.js",
"chars": 60828,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C6\\\",\\\"id\\\":\\\"main\\\"},\\\"bugFix\\\":{\\\"target\\"
},
{
"path": "src/levels/rampup/reversingChanges.js",
"chars": 66758,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22ma"
},
{
"path": "src/levels/rebase/manyRebases.js",
"chars": 22424,
"preview": "exports.level = {\n \"compareOnlyMainHashAgnostic\": true,\n \"disabledMap\": {\n \"git revert\": true,\n \"git cherry-pick"
},
{
"path": "src/levels/rebase/selectiveRebase.js",
"chars": 23993,
"preview": "exports.level = {\n \"compareAllBranchesHashAgnostic\": true,\n \"disabledMap\": {\n \"git revert\": true\n },\n \"goalTreeSt"
},
{
"path": "src/levels/remote/clone.js",
"chars": 77712,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C1\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/fakeTeamwork.js",
"chars": 53590,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C5\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/fetch.js",
"chars": 88592,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C2\\\",\\\"id\\\":\\\"main\\\"},\\\"bugFix\\\":{\\\"target\\"
},
{
"path": "src/levels/remote/fetchArgs.js",
"chars": 124503,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C6\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/fetchRebase.js",
"chars": 139354,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C3%27%22%2C%22id%22%3A%2"
},
{
"path": "src/levels/remote/lockedMain.js",
"chars": 40424,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C1\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/mergeManyFeatures.js",
"chars": 45856,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C11\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranc"
},
{
"path": "src/levels/remote/pull.js",
"chars": 56164,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C4\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/pullArgs.js",
"chars": 73207,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C6\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/push.js",
"chars": 49287,
"preview": "exports.level = {\n \"disabledMap\": {\n \"git fakeTeamwork\": true\n },\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"t"
},
{
"path": "src/levels/remote/pushArgs.js",
"chars": 74762,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C2\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/pushArgs2.js",
"chars": 67235,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C6\\\",\\\"id\\\":\\\"main\\\",\\\"remoteTrackingBranch"
},
{
"path": "src/levels/remote/pushManyFeatures.js",
"chars": 54332,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C7%27%22%2C%22id%22%3A%2"
},
{
"path": "src/levels/remote/remoteBranches.js",
"chars": 76173,
"preview": "exports.level = {\n \"goalTreeString\": \"{\\\"branches\\\":{\\\"main\\\":{\\\"target\\\":\\\"C3\\\",\\\"id\\\":\\\"main\\\"},\\\"o/main\\\":{\\\"target\\"
},
{
"path": "src/levels/remote/sourceNothing.js",
"chars": 43767,
"preview": "exports.level = {\n \"compareAllBranchesAndEnforceBranchCleanup\": true,\n \"disabledMap\": {\n \"git branch\": true,\n \"g"
},
{
"path": "src/levels/remote/tracking.js",
"chars": 123673,
"preview": "exports.level = {\n \"goalTreeString\": \"%7B%22branches%22%3A%7B%22main%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22ma"
},
{
"path": "src/style/font-awesome.css",
"chars": 14634,
"preview": "/* Font Awesome\n the iconic font designed for use with Twitter Bootstrap\n ---------------------------------------"
},
{
"path": "src/style/main.css",
"chars": 30028,
"preview": "html {\n height: 100%;\n}\n\nbody {\n margin: 0px;\n border: 0px;\n padding: 0px;\n overflow: hidden;\n}\n\nhtml, body {\n fon"
},
{
"path": "src/style/rainbows.css",
"chars": 2656,
"preview": "/*\n * CSS animated rainbow dividers of awesome\n * by Chris Heilmann @codepo8 and Lea Verou @leaverou\n**/\n\n/**\n * From CS"
},
{
"path": "src/template.index.html",
"chars": 20142,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, in"
},
{
"path": "vite.config.js",
"chars": 288,
"preview": "import { defineConfig } from 'vite';\n\nexport default defineConfig(({ command, mode, isSsrBuild, isPreview }) => {\n if ("
}
]
About this extraction
This page contains the full source code of the pcottle/learnGitBranching GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 156 files (3.3 MB), approximately 862.4k tokens, and a symbol index with 850 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.