mirror of
https://github.com/pcottle/learnGitBranching.git
synced 2025-06-27 08:28:50 +02:00
feat: command to simulate Merge/Pull Request
- Adds `git merge(M|P)R <source_branch> <target_branch> [--delete-after-merge]` command to simulate a Merge/Pull Request being merged into the target branch - The optional flag `--delete-after-merge` will remove the merged branch on the origin tree - The local remote branch remains as it is Git default behavior, even after `git fetch` (it would require `git fetch --prune` support to automatically prune remote tracking branches) - You can push a branch with a deleted remote tracking branch on origin, the ref will just update. Resolves #1057
This commit is contained in:
parent
99bc0ca8c5
commit
acffcc1616
3 changed files with 128 additions and 4 deletions
|
@ -1,5 +1,8 @@
|
||||||
var base = require('./base');
|
var base = require('./base');
|
||||||
|
var intl = require('../src/js/intl');
|
||||||
|
var Q = require('q');
|
||||||
var expectTreeAsync = base.expectTreeAsync;
|
var expectTreeAsync = base.expectTreeAsync;
|
||||||
|
var runCommand = base.runCommand;
|
||||||
|
|
||||||
describe('Git Remotes', function() {
|
describe('Git Remotes', function() {
|
||||||
it('clones', function() {
|
it('clones', function() {
|
||||||
|
@ -242,6 +245,20 @@ describe('Git Remotes', function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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() {
|
it('will not fetch if ref does not exist on remote', function() {
|
||||||
return expectTreeAsync(
|
return expectTreeAsync(
|
||||||
'git clone; git fakeTeamwork; git fetch foo:main',
|
'git clone; git fakeTeamwork; git fetch foo:main',
|
||||||
|
@ -440,5 +457,64 @@ describe('Git Remotes', function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 Q.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"}}}'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -589,6 +589,47 @@ var commandConfig = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mergeMR: {
|
||||||
|
regex: /^git +merge[MP]R($|\s)/,
|
||||||
|
options: ['--delete-after-merge'],
|
||||||
|
execute: function(engine, command) {
|
||||||
|
var generalArgs = command.getGeneralArgs();
|
||||||
|
var commandOptions = command.getOptionsMap();
|
||||||
|
if (!engine.hasOrigin()) {
|
||||||
|
throw new GitError({
|
||||||
|
msg: intl.str('git-error-origin-required'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
command.validateArgBounds(generalArgs, 2, 2);
|
||||||
|
|
||||||
|
var fromBranch = validateOriginBranchName(engine, generalArgs[0]);
|
||||||
|
var intoBranch = validateOriginBranchName(engine, generalArgs[1]);
|
||||||
|
|
||||||
|
var origin = engine.origin;
|
||||||
|
|
||||||
|
origin.checkout(intoBranch);
|
||||||
|
var mergeCommit = origin.merge(fromBranch, { noFF: true });
|
||||||
|
|
||||||
|
origin.animationFactory.genCommitBirthAnimation(
|
||||||
|
origin.animationQueue,
|
||||||
|
mergeCommit,
|
||||||
|
origin.gitVisuals
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!commandOptions['--delete-after-merge']) {
|
||||||
|
origin.validateAndDeleteBranch(fromBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
origin.checkout('main');
|
||||||
|
|
||||||
|
origin.animationFactory.playRefreshAnimationAndFinish(
|
||||||
|
origin.gitVisuals,
|
||||||
|
origin.animationQueue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
revlist: {
|
revlist: {
|
||||||
dontCountForGolf: true,
|
dontCountForGolf: true,
|
||||||
displayName: 'rev-list',
|
displayName: 'rev-list',
|
||||||
|
|
|
@ -453,10 +453,17 @@ GitEngine.prototype.findCommonAncestorWithRemote = function(originTarget) {
|
||||||
};
|
};
|
||||||
|
|
||||||
GitEngine.prototype.makeBranchOnOriginAndTrack = function(branchName, target) {
|
GitEngine.prototype.makeBranchOnOriginAndTrack = function(branchName, target) {
|
||||||
var remoteBranch = this.makeBranch(
|
var remoteBranch = this.refs[ORIGIN_PREFIX + branchName];
|
||||||
ORIGIN_PREFIX + branchName,
|
|
||||||
this.getCommitFromRef(target)
|
// If the remote branch exists but the branch on origin was deleted, updates its target location
|
||||||
);
|
if (remoteBranch) {
|
||||||
|
this.setTargetLocation(remoteBranch, target);
|
||||||
|
} else {
|
||||||
|
remoteBranch = this.makeBranch(
|
||||||
|
ORIGIN_PREFIX + branchName,
|
||||||
|
this.getCommitFromRef(target)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.refs[branchName]) { // not all remote branches have tracking ones
|
if (this.refs[branchName]) { // not all remote branches have tracking ones
|
||||||
this.setLocalToTrackRemote(this.refs[branchName], remoteBranch);
|
this.setLocalToTrackRemote(this.refs[branchName], remoteBranch);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue