Command: git move - arxanas/git-branchless GitHub Wiki

Description

Available since v0.3.5.

The git-move command is used to move commits from one place to another in the commit graph. It's helpful when dealing with multiple divergent lines of work, such as in the Divergent development workflow.

It's intended to be a complete and improved replacement for git rebase (but not git rebase --interactive). Some of the improvements:

  • It performs rebases in-memory by default, which significantly speeds up rebase operations. git rebase can only rebase commits on-disk.
  • It can move commits other than the one that's currently checked out. git rebase requires you to check out to the commit to move first.
  • It can rebase entire subtrees. git rebase only moves one linear sequence of commits at a time.
  • It warns you if your move would cause a merge conflict, but never starts merge conflict resolution unless you explicitly ask. (See Resolving merge conflicts).
  • It moves branches on any intermediate commits. git rebase only moves the branch pointing to the commit which was checked out at the time of the rebase.
  • Upstream duplicate commit detection is parallelized and thus faster. git rebase detects duplicate commits serially.
  • When you move the current commit and it exists upstream, and there is a branch pointing to that commit, the commit and branch are deleted. git rebase deletes the commit, but preserves the branch (which now points to the wrong commit).

Usage

The basic usage for git move is to specify the source and destination commits, using the following options:

  • -s <commit>/--source <commit>: This commit and all of its descendants will be moved to the destination.
  • -d <commit>/--dest <commit>: All of the commits will be moved on top of this commit.

For example:

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 30s Create bar
┣━┓
┃ ◯ 55122556 23s Create baz
┃
◯ 87b20a4b 10s Create qux

$ git move -s 55122556 -d 87b20a4b
Attempting rebase in-memory...
[1/1] Committed as: c3841da6 Create baz
branchless: processing 1 rewritten commit
In-memory rebase succeeded.

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 41s Create bar
┃
◯ 87b20a4b 21s Create qux
┃
◯ c3841da6 1s Create baz

Moving branches

Note that the --source option is used to move all descendant commits. It probably won't do the right thing if you use it with a branch, because the branch points to the last commit in a stack, not the first commit. In the following example, notice how only the last commit on the xyzzy branch is moved:

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 14m Create bar
┣━┓
┃ ◯ 55122556 14m Create baz
┃ ┃
┃ ◯ 431d09d1 12m (xyzzy) Create xyzzy
┃
◯ 87b20a4b 14m Create qux

$ git move -s xyzzy -d 87b20a4b
Attempting rebase in-memory...
[1/1] Committed as: 68f58c59 Create xyzzy
branchless: processing 1 update: branch xyzzy
branchless: processing 1 rewritten commit
In-memory rebase succeeded.

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 14m Create bar
┣━┓
┃ ◯ 55122556 14m Create baz
┃
◯ 87b20a4b 14m Create qux
┃
◯ 68f58c59 2s (xyzzy) Create xyzzy

In this case, we want to use the -b/--base option. This option essentially means "move all of the commits starting from the base commit of that line of work:

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 16m Create bar
┣━┓
┃ ◯ 55122556 16m Create baz
┃ ┃
┃ ◯ 431d09d1 15m (xyzzy) Create xyzzy
┃
◯ 87b20a4b 16m Create qux

$ git move -b xyzzy -d 87b20a4b
Attempting rebase in-memory...
[1/2] Committed as: c985de45 Create baz
[2/2] Committed as: b98d18c9 Create xyzzy
branchless: processing 1 update: branch xyzzy
branchless: processing 2 rewritten commits
In-memory rebase succeeded.

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 17m Create bar
┃
◯ 87b20a4b 16m Create qux
┃
◯ c985de45 1s Create baz
┃
◯ b98d18c9 1s (xyzzy) Create xyzzy

It often helps to think of the -b option as meaning "branch". The technical meaning is that it calculates the "merge-base" between the source and destination commits.

Moving only some commits

Available since v0.4.0.

The -s/--source options and -b/--base move entire ranges of commits. Sometimes, you only want to move a few commits without their descendants. You can use the -x/--exact flag to do this:

$ git sl
⋮
◇ 7d99c48 19s (master) Update foo
┃
◯ ec0b38b 19s Update foo 2
┃
● 538d6b0 6s Update foo 3

$ git move -x HEAD^ -d HEAD
Attempting rebase in-memory...
[1/2] Committed as: 8af8af5 Update foo 3
[2/2] Committed as: d029af1 Update foo 2
branchless: processing 2 rewritten commits
branchless: creating working copy snapshot
branchless: running command: git checkout 8af8af510c269236fe78a3281afb27ec7b1085cd
Previous HEAD position was 538d6b0 Update foo 3
branchless: processing 1 update: ref HEAD
HEAD is now at 8af8af5 Update foo 3
branchless: processing checkout
⋮
◇ 7d99c48 26s (master) Update foo
┃
● 8af8af5 0s Update foo 3
┃
◯ d029af1 0s Update foo 2
In-memory rebase succeeded.

You can pass -x/--exact multiple times to move multiple commits, or you can use a revset expression to specify multiple commits.

Inserting commits

Available since v0.4.0.

To insert a commit in between other commits, you can use the -I/--insert flag. See Insert a commit in a stack for details.

Default options

If the --dest option is not set, then it defaults to --dest HEAD. This is to aid the common case of moving another set of commits on top of this one:

$ git move -s <commit>
...moves <commit> and its descendants on top of this one...

If neither the --source or --base options are set, then it defaults to --base HEAD. This is to aid the common case of moving the current commit stack somewhere else:

$ git move -d <commit>
...moves the current commit stack on top of <commit>...

Cycles

The Git commit graph is a directed acyclic graph, so cycles between commits cannot be formed. Consequently, you cannot move a commit on top of one of its descendant commits. If you try to do so, git move will produce an error and show the problematic cycle:

$ git sl
⋮
◇ 86bdeac0 50d (master) Revert "Update foo"
┃
● ec6d42e8 19m Create bar
┃
◯ 87b20a4b 18m Create qux
┃
◯ c985de45 2m Create baz
┃
◯ b98d18c9 2m (xyzzy) Create xyzzy

$ git move -s 87b20a4b -d xyzzy
This operation failed because it would introduce a cycle:
┌─ᐅ 87b20a4b Create qux
│   c985de45 Create baz
│   b98d18c9 Create xyzzy
└── 87b20a4b Create qux

This behavior may change in the future. (Another sensible option in this case would be to reorder the commits, rather than try to apply a commit on top of itself.)

Resolving merge conflicts

Just like git rebase, git move may cause merge conflicts. However, git move will not start merge conflict resolution by default, and instead fail with a message like this:

$ git move -b bug-report -d origin/master
Attempting rebase in-memory...
[1/4] Committed as: 6656706c feat(bug_report): create `bug-report` command
[2/4] Committed as: 1eed238f feat(bug_report): use `bugreport` library to collect extra information
[3/4] Committed as: eddd5c56 temp: work on moving individual commits
This operation would cause a merge conflict:
• (1 conflicting file) 647a3971 temp: some conflicting commit
To resolve merge conflicts, retry this operation with the --merge option.

To resolve merge conflicts, try the operation again with the merge flag. You can then use git add and git rebase --continue/git rebase --abort as if you were resolving merge conflicts during a normal rebase.

⚠️ **GitHub.com Fallback** ⚠️