This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 235267
Collapse All | Expand All

(-)a/libs.git/apichanges.xml (+23 lines)
Lines 112-117 Link Here
112
        <change>
112
        <change>
113
            <api name="gitlibrary_api"/>
113
            <api name="gitlibrary_api"/>
114
            <summary>New method for updating a reference (branch) to a new commit id.</summary>
114
            <summary>New method for updating a reference (branch) to a new commit id.</summary>
115
            <version major="1" minor="27"/>
116
            <date day="1" month="7" year="2014"/>
117
            <author login="ovrabec"/>
118
            <compatibility addition="yes"/>
119
            <description>
120
                <ul>
121
                    <li>New method GitClient.cherryPick used to cherry-pick commits and apply them in the
122
                    current branch. The command may interrupt its progress and require some user actions
123
                    (such as resolve conflicts or commit) and may be continued with different kinds
124
                    of operation types passed as its arguments.</li>
125
                    <li>Introduciong new repository state: CHERRY_PICKING and CHERRY_PICKING_RESOLVED
126
                    marking the states of the repository when a cherry-picking is not finished or
127
                    it requires resolving conflicts.</li>
128
                </ul>
129
            </description>
130
            <class package="org.netbeans.libs.git" name="GitClient"/>
131
            <class package="org.netbeans.libs.git" name="GitCherryPickResult"/>
132
            <class package="org.netbeans.libs.git" name="GitRepositoryState"/>
133
            <issue number="235267"/>
134
        </change>
135
        <change>
136
            <api name="gitlibrary_api"/>
137
            <summary>New method for updating a reference (branch) to a new commit id.</summary>
115
            <version major="1" minor="26"/>
138
            <version major="1" minor="26"/>
116
            <date day="25" month="6" year="2014"/>
139
            <date day="25" month="6" year="2014"/>
117
            <author login="ovrabec"/>
140
            <author login="ovrabec"/>
(-)a/libs.git/manifest.mf (-1 / +1 lines)
Lines 1-4 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.libs.git/1
2
OpenIDE-Module: org.netbeans.libs.git/1
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties
4
OpenIDE-Module-Specification-Version: 1.26
4
OpenIDE-Module-Specification-Version: 1.27
(-)a/libs.git/src/org/netbeans/libs/git/Bundle.properties (+2 lines)
Lines 53-58 Link Here
53
LBL_RepositoryInfo_Rebasing = Rebasing
53
LBL_RepositoryInfo_Rebasing = Rebasing
54
LBL_RepositoryInfo_Apply = Apply
54
LBL_RepositoryInfo_Apply = Apply
55
LBL_RepositoryInfo_Bisecting = Bisecting
55
LBL_RepositoryInfo_Bisecting = Bisecting
56
LBL_RepositoryInfo_CherryPicking=Cherry-picking
57
LBL_RepositoryInfo_CherryPickingResolved=Cherry-picked
56
58
57
#GitException
59
#GitException
58
MSG_Exception_ObjectDoesNotExist = {0} [{1}] does not exist
60
MSG_Exception_ObjectDoesNotExist = {0} [{1}] does not exist
(-)d68b118f158f (+150 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2011 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.libs.git;
43
44
import java.io.File;
45
import java.util.Collection;
46
import java.util.Collections;
47
import java.util.List;
48
49
/**
50
 * Returned by a git cherry-pick command, represents its result.
51
 *
52
 * @author Ondra Vrabec
53
 * @since 1.27
54
 */
55
public final class GitCherryPickResult {
56
57
    private final CherryPickStatus status;
58
    private final List<File> conflicts;
59
    private final List<File> failures;
60
    private final GitRevisionInfo currentHead;
61
    private final List<GitRevisionInfo> cherryPickedCommits;
62
63
    /**
64
     * The status rebase resulted in.
65
     */
66
    public enum CherryPickStatus {
67
        /**
68
         * Command successfully finished. No action is required.
69
         */
70
        OK,
71
        /**
72
         * Command was aborted and reset to the original state. No action is
73
         * required.
74
         */
75
        ABORTED,
76
        /**
77
         * Failed because a dirty working tree prevents from starting the command.
78
         * Local modifications preventing from applying commit changes must be 
79
         * reverted.
80
         */
81
        FAILED,
82
        /**
83
         * The cherry-picking stopped in a state where it requires a manual commit.
84
         * E.g. after resolving conflicts client is required to commit the changes
85
         * before continuing with rebase.
86
         */
87
        UNCOMMITTED,
88
        /**
89
         * Conflicts when merging the cherry-picked commits.
90
         * Conflicts must be resolved and the command must be continued/aborted.
91
         */
92
        CONFLICTING;
93
    }
94
95
    GitCherryPickResult (CherryPickStatus status, List<File> conflicts, List<File> failures,
96
            GitRevisionInfo currentHead, List<GitRevisionInfo> cherryPickedCommits) {
97
        this.status = status;
98
        this.currentHead = currentHead;
99
        this.conflicts = conflicts;
100
        this.failures = failures;
101
        this.cherryPickedCommits = cherryPickedCommits;
102
    }
103
104
    /**
105
     * @return result of the cherry-pick command.
106
     */
107
    public CherryPickStatus getCherryPickStatus () {
108
        return status;
109
    }
110
111
    /**
112
     * @return current HEAD commit after the cherry-pick command.
113
     */
114
    public GitRevisionInfo getCurrentHead () {
115
        return currentHead;
116
    }
117
118
    /**
119
     * If the cherry-pick started but was unable to finish because of unresolved
120
     * conflicts then the method returns a collection of such files in conflict.
121
     * To complete the command you need to resolve the conflicts and continue the
122
     * unfinished command.
123
     *
124
     * @return files in conflict
125
     */
126
    public Collection<File> getConflicts () {
127
        return Collections.unmodifiableList(conflicts);
128
    }
129
130
    /**
131
     * When the command fails because of local modifications then this method
132
     * returns a collections of files causing the failure.
133
     *
134
     * @return files that cause the cherry-pick to fail.
135
     */
136
    public Collection<File> getFailures () {
137
        return Collections.unmodifiableList(failures);
138
    }
139
140
    /**
141
     * Returns commits cherry-picked to the current branch by the last run of the 
142
     * cherry-pick command.
143
     * 
144
     * @return array of commits cherry-picked to head.
145
     */
146
    public GitRevisionInfo[] getCherryPickedCommits () {
147
        return cherryPickedCommits.toArray(new GitRevisionInfo[cherryPickedCommits.size()]);
148
    }
149
    
150
}
(-)a/libs.git/src/org/netbeans/libs/git/GitClassFactoryImpl.java (+7 lines)
Lines 193-196 Link Here
193
        return new GitSubmoduleStatus(status, folder);
193
        return new GitSubmoduleStatus(status, folder);
194
    }
194
    }
195
195
196
    @Override
197
    public GitCherryPickResult createCherryPickResult (GitCherryPickResult.CherryPickStatus status,
198
            List<File> conflicts, List<File> failures, GitRevisionInfo head,
199
            List<GitRevisionInfo> cherryPickedCommits) {
200
        return new GitCherryPickResult(status, conflicts, failures, head, cherryPickedCommits);
201
    }
202
196
}
203
}
(-)a/libs.git/src/org/netbeans/libs/git/GitClient.java (+67 lines)
Lines 63-68 Link Here
63
import org.netbeans.libs.git.jgit.commands.CatCommand;
63
import org.netbeans.libs.git.jgit.commands.CatCommand;
64
import org.netbeans.libs.git.jgit.commands.CheckoutIndexCommand;
64
import org.netbeans.libs.git.jgit.commands.CheckoutIndexCommand;
65
import org.netbeans.libs.git.jgit.commands.CheckoutRevisionCommand;
65
import org.netbeans.libs.git.jgit.commands.CheckoutRevisionCommand;
66
import org.netbeans.libs.git.jgit.commands.CherryPickCommand;
66
import org.netbeans.libs.git.jgit.commands.CleanCommand;
67
import org.netbeans.libs.git.jgit.commands.CleanCommand;
67
import org.netbeans.libs.git.jgit.commands.CommitCommand;
68
import org.netbeans.libs.git.jgit.commands.CommitCommand;
68
import org.netbeans.libs.git.jgit.commands.CompareCommand;
69
import org.netbeans.libs.git.jgit.commands.CompareCommand;
Lines 254-259 Link Here
254
        };
255
        };
255
    }
256
    }
256
    
257
    
258
    /**
259
     * Used as a parameter of {@link #cherryPick(org.netbeans.libs.git.GitClient.CherryPickOperation, java.lang.String[], org.netbeans.libs.git.progress.ProgressMonitor) to set the behavior of the command.
260
     * @since 1.27
261
     */
262
    public enum CherryPickOperation {
263
264
        /**
265
         * A fresh cherry-pick command will be started.
266
         */
267
        BEGIN,
268
        /**
269
         * Continues an interrupted cherry-pick command after conflicts are resolved.
270
         */
271
        CONTINUE {
272
273
            @Override
274
            public String toString () {
275
                return "--continue"; //NOI18N
276
            }
277
            
278
        },
279
        /**
280
         * Tries to finish cherry-picking the current commit but stops in
281
         * cherry-picking other scheduled commits.
282
         */
283
        QUIT {
284
285
            @Override
286
            public String toString () {
287
                return "--quit"; //NOI18N
288
            }
289
            
290
        },
291
        /**
292
         * Aborts and resets an interrupted cherry-pick command.
293
         */
294
        ABORT {
295
296
            @Override
297
            public String toString () {
298
                return "--abort"; //NOI18N
299
            }
300
            
301
        };
302
    }
303
    
257
    private final JGitRepository gitRepository;
304
    private final JGitRepository gitRepository;
258
    private final Set<NotificationListener> listeners;
305
    private final Set<NotificationListener> listeners;
259
    private JGitCredentialsProvider credentialsProvider;
306
    private JGitCredentialsProvider credentialsProvider;
Lines 371-376 Link Here
371
        CheckoutRevisionCommand cmd = new CheckoutRevisionCommand(repository, getClassFactory(), revision, failOnConflict, monitor, delegateListener);
418
        CheckoutRevisionCommand cmd = new CheckoutRevisionCommand(repository, getClassFactory(), revision, failOnConflict, monitor, delegateListener);
372
        cmd.execute();
419
        cmd.execute();
373
    }
420
    }
421
    
422
    /**
423
     * Cherry-picks (transplants) selected revisions (commits) onto the current
424
     * HEAD.
425
     *
426
     * @param operation kind of cherry-pick operation you want to perform
427
     * @param revisions commits you want to cherry-pick. Makes sense only when
428
     * <code>operation</code> is set to <code>CherryPickOperation.BEGIN</code>
429
     * otherwise it's meaningless.
430
     * @param monitor progress monitor
431
     * @return result of the command
432
     * @throws GitException an unexpected error occurs
433
     * @since 1.27
434
     */
435
    public GitCherryPickResult cherryPick (CherryPickOperation operation, String[] revisions, ProgressMonitor monitor) throws GitException {
436
        Repository repository = gitRepository.getRepository();
437
        CherryPickCommand cmd = new CherryPickCommand(repository, getClassFactory(), revisions, operation, monitor, delegateListener);
438
        cmd.execute();
439
        return cmd.getResult();
440
    }
374
441
375
    /**
442
    /**
376
     * Cleans the working tree by recursively removing files that are not under 
443
     * Cleans the working tree by recursively removing files that are not under 
(-)a/libs.git/src/org/netbeans/libs/git/GitRepositoryState.java (-3 / +37 lines)
Lines 78-84 Link Here
78
            public String toString () { return Utils.getBundle(GitRepositoryState.class).getString("LBL_RepositoryInfo_Safe"); } //NOI18N
78
            public String toString () { return Utils.getBundle(GitRepositoryState.class).getString("LBL_RepositoryInfo_Safe"); } //NOI18N
79
	},
79
	},
80
80
81
	/** An unfinished merge or cherry-picking. Must resolve or reset before continuing normally
81
	/**
82
     * An unfinished merge. Must resolve or reset before continuing normally
82
	 */
83
	 */
83
	MERGING {
84
	MERGING {
84
            @Override
85
            @Override
Lines 107-112 Link Here
107
	},
108
	},
108
109
109
	/**
110
	/**
111
     * An unfinished cherry-pick. Must resolve or reset before continuing normally
112
     * @since 1.27
113
	 */
114
	CHERRY_PICKING {
115
            @Override
116
            public boolean canCheckout () { return false; }
117
            @Override
118
            public boolean canResetHead () { return true; }
119
            @Override
120
            public boolean canCommit () { return false; }
121
            @Override
122
            public String toString () { return Utils.getBundle(GitRepositoryState.class).getString("LBL_RepositoryInfo_CherryPicking"); } //NOI18N
123
	},
124
125
	/**
126
	 * A cherry-picked commit where all conflicts have been resolved. The index does not
127
	 * contain any unmerged paths and the repository requires a commit.
128
     * @since 1.27
129
	 */
130
	CHERRY_PICKING_RESOLVED {
131
            @Override
132
            public boolean canCheckout () { return true; }
133
            @Override
134
            public boolean canResetHead () { return true; }
135
            @Override
136
            public boolean canCommit () { return true; }
137
            @Override
138
            public String toString () { return Utils.getBundle(GitRepositoryState.class).getString("LBL_RepositoryInfo_CherryPickingResolved"); } //NOI18N
139
	},
140
141
	/**
110
	 * An unfinished rebase or am. Must resolve, skip or abort before normal work can take place
142
	 * An unfinished rebase or am. Must resolve, skip or abort before normal work can take place
111
	 */
143
	 */
112
	REBASING {
144
	REBASING {
Lines 178-188 Link Here
178
                case BISECTING:
210
                case BISECTING:
179
                    return GitRepositoryState.BISECTING;
211
                    return GitRepositoryState.BISECTING;
180
                case MERGING:
212
                case MERGING:
181
                case CHERRY_PICKING:
182
                case REVERTING:
213
                case REVERTING:
183
                    return GitRepositoryState.MERGING;
214
                    return GitRepositoryState.MERGING;
215
                case CHERRY_PICKING:
216
                    return GitRepositoryState.CHERRY_PICKING;
217
                case CHERRY_PICKING_RESOLVED:
218
                    return GitRepositoryState.CHERRY_PICKING_RESOLVED;
184
                case MERGING_RESOLVED:
219
                case MERGING_RESOLVED:
185
                case CHERRY_PICKING_RESOLVED:
186
                case REVERTING_RESOLVED:
220
                case REVERTING_RESOLVED:
187
                    return GitRepositoryState.MERGING_RESOLVED;
221
                    return GitRepositoryState.MERGING_RESOLVED;
188
                case REBASING:
222
                case REBASING:
(-)a/libs.git/src/org/netbeans/libs/git/jgit/GitClassFactory.java (+5 lines)
Lines 62-67 Link Here
62
import org.eclipse.jgit.transport.URIish;
62
import org.eclipse.jgit.transport.URIish;
63
import org.netbeans.libs.git.GitBlameResult;
63
import org.netbeans.libs.git.GitBlameResult;
64
import org.netbeans.libs.git.GitBranch;
64
import org.netbeans.libs.git.GitBranch;
65
import org.netbeans.libs.git.GitCherryPickResult;
65
import org.netbeans.libs.git.GitConflictDescriptor;
66
import org.netbeans.libs.git.GitConflictDescriptor;
66
import org.netbeans.libs.git.GitConflictDescriptor.Type;
67
import org.netbeans.libs.git.GitConflictDescriptor.Type;
67
import org.netbeans.libs.git.GitMergeResult;
68
import org.netbeans.libs.git.GitMergeResult;
Lines 89-94 Link Here
89
    
90
    
90
    public abstract GitBranch createBranch (String name, boolean remote, boolean active, ObjectId id);
91
    public abstract GitBranch createBranch (String name, boolean remote, boolean active, ObjectId id);
91
92
93
    public abstract GitCherryPickResult createCherryPickResult (
94
            GitCherryPickResult.CherryPickStatus status, List<File> conflicts,
95
            List<File> failures, GitRevisionInfo head, List<GitRevisionInfo> cherryPickedCommits);
96
92
    public abstract GitConflictDescriptor createConflictDescriptor (Type type);
97
    public abstract GitConflictDescriptor createConflictDescriptor (Type type);
93
98
94
    public abstract GitFileInfo createFileInfo (File file, String oldPath, GitFileInfo.Status status, File originalFile, String originalPath);
99
    public abstract GitFileInfo createFileInfo (File file, String oldPath, GitFileInfo.Status status, File originalFile, String originalPath);
(-)a/libs.git/src/org/netbeans/libs/git/jgit/Utils.java (-6 / +10 lines)
Lines 263-278 Link Here
263
    }
263
    }
264
    
264
    
265
    public static RevCommit findCommit (Repository repository, String revision, RevWalk walk) throws GitException.MissingObjectException, GitException {
265
    public static RevCommit findCommit (Repository repository, String revision, RevWalk walk) throws GitException.MissingObjectException, GitException {
266
        ObjectId commitId = parseObjectId(repository, revision);
267
        if (commitId == null) {
268
            throw new GitException.MissingObjectException(revision, GitObjectType.COMMIT);
269
        }
270
        return findCommit(repository, commitId, walk);
271
    }
272
    
273
    public static RevCommit findCommit (Repository repository, ObjectId commitId, RevWalk walk) throws GitException.MissingObjectException, GitException {
266
        try {
274
        try {
267
            ObjectId commitId = parseObjectId(repository, revision);
268
            if (commitId == null) {
269
                throw new GitException.MissingObjectException(revision, GitObjectType.COMMIT);
270
            }
271
            return (walk == null ? new RevWalk(repository) : walk).parseCommit(commitId);
275
            return (walk == null ? new RevWalk(repository) : walk).parseCommit(commitId);
272
        } catch (MissingObjectException ex) {
276
        } catch (MissingObjectException ex) {
273
            throw new GitException.MissingObjectException(revision, GitObjectType.COMMIT, ex);
277
            throw new GitException.MissingObjectException(commitId.name(), GitObjectType.COMMIT, ex);
274
        } catch (IncorrectObjectTypeException ex) {
278
        } catch (IncorrectObjectTypeException ex) {
275
            throw new GitException(MessageFormat.format(Utils.getBundle(Utils.class).getString("MSG_Exception_IdNotACommit"), revision)); //NOI18N
279
            throw new GitException(MessageFormat.format(Utils.getBundle(Utils.class).getString("MSG_Exception_IdNotACommit"), commitId.name())); //NOI18N
276
        } catch (IOException ex) {
280
        } catch (IOException ex) {
277
            throw new GitException(ex);
281
            throw new GitException(ex);
278
        }
282
        }
(-)a/libs.git/src/org/netbeans/libs/git/jgit/commands/Bundle.properties (+1 lines)
Lines 54-59 Link Here
54
MSG_Error_RepositoryExists = Git repository already exists at {0}: {1}
54
MSG_Error_RepositoryExists = Git repository already exists at {0}: {1}
55
MSG_Error_CannotCatRoot = Cannot cat root: {0}
55
MSG_Error_CannotCatRoot = Cannot cat root: {0}
56
MSG_Error_Commit_ConflictsInIndex = Index contains files in conflict, please resolve them before commit
56
MSG_Error_Commit_ConflictsInIndex = Index contains files in conflict, please resolve them before commit
57
MSG_Error_Commit_CannotAmend=Cannot amend a commit in this repository state.
57
MSG_Error_Commit_PartialCommitAfterMerge = Cannot do a partial commit during a merge.
58
MSG_Error_Commit_PartialCommitAfterMerge = Cannot do a partial commit during a merge.
58
MSG_Error_Commit_NotAllowedInCurrentState = Cannot commit in current state: {0}
59
MSG_Error_Commit_NotAllowedInCurrentState = Cannot commit in current state: {0}
59
MSG_Error_UpdateTracking_InvalidReference=Invalid reference: {0}
60
MSG_Error_UpdateTracking_InvalidReference=Invalid reference: {0}
(-)d68b118f158f (+426 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2010 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.libs.git.jgit.commands;
43
44
import java.io.BufferedOutputStream;
45
import java.io.File;
46
import java.io.FileOutputStream;
47
import java.io.IOException;
48
import java.util.ArrayList;
49
import java.util.Collection;
50
import java.util.Collections;
51
import java.util.Iterator;
52
import java.util.List;
53
import java.util.Map;
54
import java.util.logging.Level;
55
import java.util.logging.Logger;
56
import org.eclipse.jgit.api.CherryPickResult;
57
import org.eclipse.jgit.api.Git;
58
import org.eclipse.jgit.api.RebaseCommand.Operation;
59
import org.eclipse.jgit.api.errors.GitAPIException;
60
import org.eclipse.jgit.api.errors.JGitInternalException;
61
import org.eclipse.jgit.lib.Constants;
62
import org.eclipse.jgit.lib.ObjectId;
63
import org.eclipse.jgit.lib.ObjectReader;
64
import org.eclipse.jgit.lib.RebaseTodoFile;
65
import org.eclipse.jgit.lib.RebaseTodoLine;
66
import org.eclipse.jgit.lib.Ref;
67
import org.eclipse.jgit.lib.Repository;
68
import org.eclipse.jgit.lib.RepositoryState;
69
import org.eclipse.jgit.merge.ResolveMerger;
70
import org.eclipse.jgit.revwalk.RevCommit;
71
import org.eclipse.jgit.revwalk.RevWalk;
72
import org.eclipse.jgit.util.IO;
73
import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
74
import org.netbeans.libs.git.GitCherryPickResult;
75
import org.netbeans.libs.git.GitClient;
76
import org.netbeans.libs.git.GitException;
77
import org.netbeans.libs.git.GitRevisionInfo;
78
import org.netbeans.libs.git.GitStatus;
79
import org.netbeans.libs.git.jgit.DelegatingGitProgressMonitor;
80
import org.netbeans.libs.git.jgit.GitClassFactory;
81
import org.netbeans.libs.git.jgit.Utils;
82
import org.netbeans.libs.git.progress.FileListener;
83
import org.netbeans.libs.git.progress.ProgressMonitor;
84
import org.netbeans.libs.git.progress.StatusListener;
85
86
/**
87
 *
88
 * @author ondra
89
 */
90
public class CherryPickCommand extends GitCommand {
91
92
    private final String[] revisions;
93
    private GitCherryPickResult result;
94
    private final ProgressMonitor monitor;
95
    private final GitClient.CherryPickOperation operation;
96
    private final FileListener listener;
97
    private static final String SEQUENCER = "sequencer";
98
    private static final String SEQUENCER_HEAD = "head";
99
    private static final String SEQUENCER_TODO = "todo";
100
101
    public CherryPickCommand (Repository repository, GitClassFactory gitFactory, String[] revisions,
102
            GitClient.CherryPickOperation operation, ProgressMonitor monitor, FileListener listener) {
103
        super(repository, gitFactory, monitor);
104
        this.revisions = revisions;
105
        this.operation = operation;
106
        this.monitor = monitor;
107
        this.listener = listener;
108
    }
109
110
    @Override
111
    protected void run () throws GitException {
112
        Repository repository = getRepository();
113
        ObjectId originalCommit = getOriginalCommit();
114
        ObjectId head = getHead();
115
        List<RebaseTodoLine> steps;
116
        try {
117
            switch (operation) {
118
                case BEGIN:
119
                    // initialize sequencer and cherry-pick steps if there are
120
                    // more commits to cherry-pick
121
                    steps = prepareCommand(head);
122
                    // apply the selected steps
123
                    applySteps(steps, false);
124
                    break;
125
                case ABORT:
126
                    // delete the sequencer and reset to the original head
127
                    if (repository.getRepositoryState() == RepositoryState.CHERRY_PICKING
128
                            || repository.getRepositoryState() == RepositoryState.CHERRY_PICKING_RESOLVED) {
129
                        if (originalCommit == null) {
130
                            // maybe the sequencer is not created in that case simply reset to HEAD
131
                            originalCommit = head;
132
                        }
133
                    }
134
                    Utils.deleteRecursively(getSequencerFolder());
135
                    if (originalCommit != null) {
136
                        ResetCommand reset = new ResetCommand(repository, getClassFactory(),
137
                                originalCommit.name(), GitClient.ResetType.HARD, new DelegatingGitProgressMonitor(monitor), listener);
138
                        reset.execute();
139
                    }
140
                    result = createCustomResult(GitCherryPickResult.CherryPickStatus.ABORTED);
141
                    break;
142
                case QUIT:
143
                    // used to reset the sequencer only
144
                    Utils.deleteRecursively(getSequencerFolder());
145
                    switch (repository.getRepositoryState()) {
146
                        case CHERRY_PICKING:
147
                            // unresolved conflicts
148
                            result = createResult(CherryPickResult.CONFLICT);
149
                            break;
150
                        case CHERRY_PICKING_RESOLVED:
151
                            result = createCustomResult(GitCherryPickResult.CherryPickStatus.UNCOMMITTED);
152
                            break;
153
                        default:
154
                            result = createCustomResult(GitCherryPickResult.CherryPickStatus.OK);
155
                            break;
156
                    }
157
                    break;
158
                case CONTINUE:
159
                    switch (repository.getRepositoryState()) {
160
                        case CHERRY_PICKING:
161
                            // unresolved conflicts, cannot continue
162
                            result = createResult(CherryPickResult.CONFLICT);
163
                            break;
164
                        case CHERRY_PICKING_RESOLVED:
165
                            // cannot continue without manual commit
166
                            result = createCustomResult(GitCherryPickResult.CherryPickStatus.UNCOMMITTED);
167
                            break;
168
                        default:
169
                            // read steps from sequencer and apply them
170
                            // if sequencer is empty this will be a noop
171
                            steps = readTodoFile(repository);
172
                            applySteps(steps, true);
173
                            break;
174
                    }
175
                    break;
176
                default:
177
                    throw new IllegalStateException("Unexpected operation " + operation.name());
178
            }
179
        } catch (GitAPIException | IOException ex) {
180
            throw new GitException(ex);
181
        }
182
    }
183
184
    @Override
185
    protected String getCommandDescription () {
186
        StringBuilder sb = new StringBuilder();
187
        sb.append("git cherry-pick "); //NOI18N
188
        if (operation == GitClient.CherryPickOperation.BEGIN) {
189
            for (String rev : revisions) {
190
                sb.append(rev).append(" "); //NOI18N
191
            }
192
        } else {
193
            sb.append(operation.toString());
194
        }
195
        return sb.toString();
196
    }
197
198
    public GitCherryPickResult getResult () {
199
        return result;
200
    }
201
202
    static Operation getOperation (GitClient.RebaseOperationType operation) {
203
        return Operation.valueOf(operation.name());
204
    }
205
206
    private void applySteps (List<RebaseTodoLine> steps, boolean skipFirstStep) throws GitAPIException, IOException {
207
        Repository repository = getRepository();
208
        ObjectReader or = repository.newObjectReader();
209
        CherryPickResult res = null;
210
        boolean skipped = false;
211
        List<Ref> cherryPickedRefs = new ArrayList<>();
212
        for (Iterator<RebaseTodoLine> it = steps.iterator(); it.hasNext(); ) {
213
            RebaseTodoLine step = it.next();
214
            if (step.getAction() == RebaseTodoLine.Action.PICK) {
215
                if (skipFirstStep && !skipped) {
216
                    it.remove();
217
                    skipped = true;
218
                    continue;
219
                }
220
                Collection<ObjectId> ids = or.resolve(step.getCommit());
221
                if (ids.size() != 1) {
222
                    throw new JGitInternalException("Could not resolve uniquely the abbreviated object ID");
223
                }
224
                org.eclipse.jgit.api.CherryPickCommand command = new Git(repository).cherryPick();
225
                command.include(ids.iterator().next());
226
                res = command.call();
227
                if (res.getStatus() == CherryPickResult.CherryPickStatus.OK) {
228
                    it.remove();
229
                    writeTodoFile(repository, steps);
230
                    cherryPickedRefs.addAll(res.getCherryPickedRefs());
231
                } else {
232
                    break;
233
                }
234
            } else {
235
                it.remove();
236
            }
237
        }
238
        if (res == null) {
239
            result = createCustomResult(GitCherryPickResult.CherryPickStatus.OK, cherryPickedRefs);
240
        } else {
241
            result = createResult(res, cherryPickedRefs);
242
        }
243
        if (steps.isEmpty()) {
244
            // sequencer no longer needed
245
            Utils.deleteRecursively(getSequencerFolder());
246
        }
247
    }
248
249
    private GitCherryPickResult createResult (CherryPickResult res) {
250
        return createResult(res, Collections.<Ref>emptyList());
251
    }
252
    
253
    private GitCherryPickResult createResult (CherryPickResult res, List<Ref> cherryPickedRefs) {
254
        GitRevisionInfo currHead = getCurrentHead();
255
        
256
        GitCherryPickResult.CherryPickStatus status = GitCherryPickResult.CherryPickStatus.valueOf(res.getStatus().name());
257
        List<File> conflicts;
258
        if (res.getStatus() == CherryPickResult.CherryPickStatus.CONFLICTING) {
259
            conflicts = getConflicts(currHead);
260
        } else {
261
            conflicts = Collections.<File>emptyList();
262
        }
263
        List<GitRevisionInfo> commits = toCommits(cherryPickedRefs);
264
        return getClassFactory().createCherryPickResult(status, conflicts, getFailures(res), currHead, commits);
265
    }
266
267
    private List<GitRevisionInfo> toCommits (List<Ref> cherryPickedRefs) {
268
        List<GitRevisionInfo> commits = new ArrayList<>(cherryPickedRefs.size());
269
        Repository repository = getRepository();
270
        RevWalk walk = new RevWalk(repository);
271
        for (Ref ref : cherryPickedRefs) {
272
            try {
273
                commits.add(getClassFactory().createRevisionInfo(Utils.findCommit(repository,
274
                        ref.getLeaf().getObjectId(), walk), repository));
275
            } catch (GitException ex) {
276
                Logger.getLogger(CherryPickCommand.class.getName()).log(Level.INFO, null, ex);
277
            }
278
        }
279
        return commits;
280
    }
281
282
    private GitRevisionInfo getCurrentHead () {
283
        GitRevisionInfo currHead;
284
        Repository repository = getRepository();
285
        try {
286
            currHead = getClassFactory().createRevisionInfo(Utils.findCommit(repository, Constants.HEAD), repository);
287
        } catch (GitException ex) {
288
            currHead = null;
289
        }
290
        return currHead;
291
    }
292
293
    private GitCherryPickResult createCustomResult (GitCherryPickResult.CherryPickStatus status) {
294
        return createCustomResult(status, Collections.<Ref>emptyList());
295
    }
296
297
    private GitCherryPickResult createCustomResult (GitCherryPickResult.CherryPickStatus status, List<Ref> cherryPickedRefs) {
298
        return getClassFactory().createCherryPickResult(status, Collections.<File>emptyList(),
299
                Collections.<File>emptyList(), getCurrentHead(), toCommits(cherryPickedRefs));
300
    }
301
302
    private List<File> getConflicts (GitRevisionInfo info) {
303
        List<File> conflicts;
304
        try {
305
            Repository repository = getRepository();
306
            Map<File, GitRevisionInfo.GitFileInfo> modifiedFiles = info.getModifiedFiles();
307
            ConflictCommand cmd = new ConflictCommand(repository, getClassFactory(), modifiedFiles.keySet().toArray(
308
                    new File[modifiedFiles.keySet().size()]),
309
                    new DelegatingGitProgressMonitor(monitor),
310
                    new StatusListener() {
311
                        @Override
312
                        public void notifyStatus (GitStatus status) { }
313
                    });
314
            cmd.execute();
315
            Map<File, GitStatus> statuses = cmd.getStatuses();
316
            conflicts = new ArrayList<>(statuses.size());
317
            for (Map.Entry<File, GitStatus> e : statuses.entrySet()) {
318
                if (e.getValue().isConflict()) {
319
                    conflicts.add(e.getKey());
320
                }
321
            }
322
        } catch (GitException ex) {
323
            Logger.getLogger(CherryPickCommand.class.getName()).log(Level.INFO, null, ex);
324
            conflicts = Collections.<File>emptyList();
325
        }
326
        return conflicts;
327
    }
328
329
    private List<File> getFailures (CherryPickResult result) {
330
        List<File> files = new ArrayList<>();
331
        File workDir = getRepository().getWorkTree();
332
        if (result.getStatus() == CherryPickResult.CherryPickStatus.FAILED) {
333
            Map<String, ResolveMerger.MergeFailureReason> obstructions = result.getFailingPaths();
334
            if (obstructions != null) {
335
                for (Map.Entry<String, ResolveMerger.MergeFailureReason> failure : obstructions.entrySet()) {
336
                    files.add(new File(workDir, failure.getKey()));
337
                }
338
            }
339
        }
340
        return Collections.unmodifiableList(files);
341
    }
342
343
    private File getSequencerFolder () {
344
        return new File(getRepository().getDirectory(), SEQUENCER);
345
    }
346
347
    private ObjectId getOriginalCommit () throws GitException {
348
        Repository repository = getRepository();
349
        File seqHead = new File(getSequencerFolder(), SEQUENCER_HEAD);
350
        ObjectId originalCommitId = null;
351
        if (seqHead.canRead()) {
352
            try {
353
                byte[] content = IO.readFully(seqHead);
354
                if (content.length > 0) {
355
                    originalCommitId = ObjectId.fromString(content, 0);
356
                }
357
                if (originalCommitId != null) {
358
                    originalCommitId = repository.resolve(originalCommitId.getName() + "^{commit}");
359
                }
360
            } catch (IOException e) {
361
            }
362
        }
363
        return originalCommitId;
364
    }
365
366
	private ObjectId getHead () throws GitException {
367
		return Utils.findCommit(getRepository(), Constants.HEAD);
368
	}
369
370
    private List<RebaseTodoLine> prepareCommand (ObjectId head) throws GitException, IOException {
371
        Repository repository = getRepository();
372
        ObjectReader or = repository.newObjectReader();
373
        RevWalk walk = new RevWalk(or);
374
        List<RevCommit> commits = new ArrayList<>(revisions.length);
375
        for (String rev : revisions) {
376
            RevCommit commit = Utils.findCommit(repository, rev, walk);
377
            commits.add(commit);
378
        }
379
        List<RebaseTodoLine> steps = new ArrayList<>(commits.size());
380
        if (commits.size() == 1) {
381
            RevCommit commit = commits.get(0);
382
            steps.add(new RebaseTodoLine(RebaseTodoLine.Action.PICK,
383
                    or.abbreviate(commit), commit.getShortMessage()));
384
        } else if (!commits.isEmpty()) {
385
            File sequencer = getSequencerFolder();
386
            sequencer.mkdirs();
387
            try {
388
                for (RevCommit commit : commits) {
389
                    steps.add(new RebaseTodoLine(RebaseTodoLine.Action.PICK,
390
                            or.abbreviate(commit), commit.getShortMessage()));
391
                }
392
                writeTodoFile(repository, steps);
393
                writeFile(new File(sequencer, SEQUENCER_HEAD), head);
394
            } catch (IOException ex) {
395
                Utils.deleteRecursively(sequencer);
396
                throw new GitException(ex);
397
            }
398
        }
399
        return steps;
400
    }
401
    
402
    private void writeFile (File file, ObjectId id) throws IOException {
403
        try (BufferedOutputStream bos = new SafeBufferedOutputStream(new FileOutputStream(file))) {
404
            id.copyTo(bos);
405
            bos.write('\n');
406
        }
407
    }
408
409
    private void writeTodoFile (Repository repository, List<RebaseTodoLine> steps) throws IOException {
410
        File f = new File(repository.getDirectory(), SEQUENCER);
411
        if (f.canWrite()) {
412
            RebaseTodoFile todoFile = new RebaseTodoFile(repository);
413
            todoFile.writeRebaseTodoFile(SEQUENCER + File.separator + SEQUENCER_TODO, steps, false);
414
        }
415
    }
416
417
    private List<RebaseTodoLine> readTodoFile (Repository repository) throws IOException {
418
        String path = SEQUENCER + File.separator + SEQUENCER_TODO;
419
        File f = new File(repository.getDirectory(), path);
420
        if (f.canRead()) {
421
            RebaseTodoFile todoFile = new RebaseTodoFile(repository);
422
            return todoFile.readRebaseTodo(SEQUENCER + File.separator + SEQUENCER_TODO, true);
423
        }
424
        return Collections.<RebaseTodoLine>emptyList();
425
    }
426
}
(-)a/libs.git/src/org/netbeans/libs/git/jgit/commands/CommitCommand.java (-18 / +22 lines)
Lines 57-63 Link Here
57
import org.eclipse.jgit.dircache.DirCacheIterator;
57
import org.eclipse.jgit.dircache.DirCacheIterator;
58
import org.eclipse.jgit.errors.CorruptObjectException;
58
import org.eclipse.jgit.errors.CorruptObjectException;
59
import org.eclipse.jgit.errors.NoWorkTreeException;
59
import org.eclipse.jgit.errors.NoWorkTreeException;
60
import org.eclipse.jgit.errors.UnmergedPathException;
61
import org.eclipse.jgit.lib.Constants;
60
import org.eclipse.jgit.lib.Constants;
62
import org.eclipse.jgit.lib.FileMode;
61
import org.eclipse.jgit.lib.FileMode;
63
import org.eclipse.jgit.lib.ObjectId;
62
import org.eclipse.jgit.lib.ObjectId;
Lines 105-115 Link Here
105
        boolean retval = super.prepareCommand();
104
        boolean retval = super.prepareCommand();
106
        if (retval) {
105
        if (retval) {
107
            RepositoryState state = getRepository().getRepositoryState();
106
            RepositoryState state = getRepository().getRepositoryState();
108
            if (RepositoryState.MERGING.equals(state)) {
107
            if (amend && !state.canAmend()) {
108
                String errorMessage = Utils.getBundle(CommitCommand.class).getString("MSG_Error_Commit_CannotAmend"); //NOI18N
109
                monitor.preparationsFailed(errorMessage);
110
                throw new GitException(errorMessage);
111
            }
112
            if (RepositoryState.MERGING.equals(state) || RepositoryState.CHERRY_PICKING.equals(state)) {
109
                String errorMessage = Utils.getBundle(CommitCommand.class).getString("MSG_Error_Commit_ConflictsInIndex"); //NOI18N
113
                String errorMessage = Utils.getBundle(CommitCommand.class).getString("MSG_Error_Commit_ConflictsInIndex"); //NOI18N
110
                monitor.preparationsFailed(errorMessage);
114
                monitor.preparationsFailed(errorMessage);
111
                throw new GitException(errorMessage);
115
                throw new GitException(errorMessage);
112
            } else if (RepositoryState.MERGING_RESOLVED.equals(state) && roots.length > 0) {
116
            } else if ((RepositoryState.MERGING_RESOLVED.equals(state)
117
                    || RepositoryState.CHERRY_PICKING_RESOLVED.equals(state)) && roots.length > 0) {
113
                boolean fullWorkingTree = false;
118
                boolean fullWorkingTree = false;
114
                File repositoryRoot = getRepository().getWorkTree();
119
                File repositoryRoot = getRepository().getWorkTree();
115
                for (File root : roots) {
120
                for (File root : roots) {
Lines 149-158 Link Here
149
                if(commiter != null) {                    
154
                if(commiter != null) {                    
150
                    commit.setCommitter(commiter.getName(), commiter.getEmailAddress());
155
                    commit.setCommitter(commiter.getName(), commiter.getEmailAddress());
151
                }
156
                }
152
                if (amend) {
157
                setAuthorshipIfNeeded(repository, commit);
153
                    RevCommit lastCommit = Utils.findCommit(repository, "HEAD^{commit}");
154
                    transferTimestamp(commit, lastCommit);
155
                }
156
                
158
                
157
                commit.setMessage(message);
159
                commit.setMessage(message);
158
                commit.setAmend(amend);
160
                commit.setAmend(amend);
Lines 170-187 Link Here
170
                    }
172
                    }
171
                }
173
                }
172
            }
174
            }
173
        } catch (GitAPIException ex) {
175
        } catch (GitAPIException | JGitInternalException | NoWorkTreeException | IOException ex) {
174
            throw new GitException(ex);
176
            throw new GitException(ex);
175
        } catch (UnmergedPathException ex) {
177
        }
176
            throw new GitException(ex);
178
    }
177
        } catch (JGitInternalException ex) {
179
178
            throw new GitException(ex);
180
    private void setAuthorshipIfNeeded (Repository repository, org.eclipse.jgit.api.CommitCommand cmd)
179
        } catch (NoWorkTreeException ex) {
181
            throws GitException, NoWorkTreeException, IOException {
180
            throw new GitException(ex);
182
        if (amend) {
181
        } catch (CorruptObjectException ex) {
183
            RevCommit lastCommit = Utils.findCommit(repository, "HEAD^{commit}");
182
            throw new GitException(ex);
184
            transferTimestamp(cmd, lastCommit);
183
        } catch (IOException ex) {
185
        }
184
            throw new GitException(ex);
186
        if (repository.getRepositoryState() == RepositoryState.CHERRY_PICKING_RESOLVED) {
187
            RevCommit lastCommit = Utils.findCommit(repository, repository.readCherryPickHead(), null);
188
            transferTimestamp(cmd, lastCommit);
185
        }
189
        }
186
    }
190
    }
187
191
(-)a/libs.git/src/org/netbeans/libs/git/jgit/commands/RebaseCommand.java (-2 / +2 lines)
Lines 162-169 Link Here
162
            Repository repository = getRepository();
162
            Repository repository = getRepository();
163
            GitRevisionInfo info = getClassFactory().createRevisionInfo(currentCommit, repository);
163
            GitRevisionInfo info = getClassFactory().createRevisionInfo(currentCommit, repository);
164
            Map<File, GitRevisionInfo.GitFileInfo> modifiedFiles = info.getModifiedFiles();
164
            Map<File, GitRevisionInfo.GitFileInfo> modifiedFiles = info.getModifiedFiles();
165
            StatusCommand cmd = new StatusCommand(repository, Constants.HEAD, modifiedFiles.keySet().toArray(
165
            ConflictCommand cmd = new ConflictCommand(repository, getClassFactory(), modifiedFiles.keySet().toArray(
166
                    new File[modifiedFiles.keySet().size()]), getClassFactory(), 
166
                    new File[modifiedFiles.keySet().size()]),
167
                    new DelegatingGitProgressMonitor(monitor),
167
                    new DelegatingGitProgressMonitor(monitor),
168
                    new StatusListener() {
168
                    new StatusListener() {
169
                        @Override
169
                        @Override
(-)a/libs.git/test/unit/src/org/netbeans/libs/git/GitEnumsStateTest.java (+7 lines)
Lines 43-48 Link Here
43
package org.netbeans.libs.git;
43
package org.netbeans.libs.git;
44
44
45
import java.io.IOException;
45
import java.io.IOException;
46
import org.eclipse.jgit.api.CherryPickResult;
46
import org.eclipse.jgit.api.MergeResult;
47
import org.eclipse.jgit.api.MergeResult;
47
import org.eclipse.jgit.api.RebaseResult;
48
import org.eclipse.jgit.api.RebaseResult;
48
import org.eclipse.jgit.lib.RefUpdate;
49
import org.eclipse.jgit.lib.RefUpdate;
Lines 96-99 Link Here
96
            assertNotNull(GitSubmoduleStatus.parseStatus(status));
97
            assertNotNull(GitSubmoduleStatus.parseStatus(status));
97
        }
98
        }
98
    }
99
    }
100
    
101
    public void testCherryPickStatus () {
102
        for (CherryPickResult.CherryPickStatus status : CherryPickResult.CherryPickStatus.values()) {
103
            assertNotNull(GitCherryPickResult.CherryPickStatus.valueOf(status.name()));
104
        }
105
    }
99
}
106
}
(-)a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/CommandsTestSuite.java (+2 lines)
Lines 51-56 Link Here
51
import org.netbeans.libs.git.jgit.commands.BranchTest;
51
import org.netbeans.libs.git.jgit.commands.BranchTest;
52
import org.netbeans.libs.git.jgit.commands.CatTest;
52
import org.netbeans.libs.git.jgit.commands.CatTest;
53
import org.netbeans.libs.git.jgit.commands.CheckoutTest;
53
import org.netbeans.libs.git.jgit.commands.CheckoutTest;
54
import org.netbeans.libs.git.jgit.commands.CherryPickTest;
54
import org.netbeans.libs.git.jgit.commands.CleanTest;
55
import org.netbeans.libs.git.jgit.commands.CleanTest;
55
import org.netbeans.libs.git.jgit.commands.CommitTest;
56
import org.netbeans.libs.git.jgit.commands.CommitTest;
56
import org.netbeans.libs.git.jgit.commands.CompareCommitTest;
57
import org.netbeans.libs.git.jgit.commands.CompareCommitTest;
Lines 100-105 Link Here
100
        suite.addTestSuite(BranchTest.class);
101
        suite.addTestSuite(BranchTest.class);
101
        suite.addTestSuite(CatTest.class);
102
        suite.addTestSuite(CatTest.class);
102
        suite.addTestSuite(CheckoutTest.class);
103
        suite.addTestSuite(CheckoutTest.class);
104
        suite.addTestSuite(CherryPickTest.class);
103
        suite.addTestSuite(CleanTest.class);
105
        suite.addTestSuite(CleanTest.class);
104
        suite.addTestSuite(CommitTest.class);
106
        suite.addTestSuite(CommitTest.class);
105
        suite.addTestSuite(CompareCommitTest.class);
107
        suite.addTestSuite(CompareCommitTest.class);
(-)d68b118f158f (+359 lines)
Added Link Here
1
package org.netbeans.libs.git.jgit.commands;
2
3
/*
4
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5
 *
6
 * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
7
 *
8
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
9
 * Other names may be trademarks of their respective owners.
10
 *
11
 * The contents of this file are subject to the terms of either the GNU
12
 * General Public License Version 2 only ("GPL") or the Common
13
 * Development and Distribution License("CDDL") (collectively, the
14
 * "License"). You may not use this file except in compliance with the
15
 * License. You can obtain a copy of the License at
16
 * http://www.netbeans.org/cddl-gplv2.html
17
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
18
 * specific language governing permissions and limitations under the
19
 * License.  When distributing the software, include this License Header
20
 * Notice in each file and include the License file at
21
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
22
 * particular file as subject to the "Classpath" exception as provided
23
 * by Oracle in the GPL Version 2 section of the License file that
24
 * accompanied this code. If applicable, add the following below the
25
 * License Header, with the fields enclosed by brackets [] replaced by
26
 * your own identifying information:
27
 * "Portions Copyrighted [year] [name of copyright owner]"
28
 *
29
 * If you wish your version of this file to be governed by only the CDDL
30
 * or only the GPL Version 2, indicate your decision by adding
31
 * "[Contributor] elects to include this software in this distribution
32
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
33
 * single choice of license, a recipient has the option to distribute
34
 * your version of this file under either the CDDL, the GPL Version 2 or
35
 * to extend the choice of license to its licensees as provided above.
36
 * However, if you add GPL Version 2 code and therefore, elected the GPL
37
 * Version 2 license, then the option applies only if the new code is
38
 * made subject to such option by the copyright holder.
39
 *
40
 * Contributor(s):
41
 *
42
 * Portions Copyrighted 2010 Sun Microsystems, Inc.
43
 */
44
45
import java.io.File;
46
import java.io.IOException;
47
import org.eclipse.jgit.lib.Constants;
48
import org.eclipse.jgit.lib.ObjectReader;
49
import org.eclipse.jgit.lib.Repository;
50
import org.eclipse.jgit.lib.RepositoryState;
51
import org.eclipse.jgit.revwalk.RevCommit;
52
import org.netbeans.libs.git.GitCherryPickResult;
53
import org.netbeans.libs.git.GitClient;
54
import org.netbeans.libs.git.GitRevisionInfo;
55
import org.netbeans.libs.git.GitStatus.Status;
56
import org.netbeans.libs.git.SearchCriteria;
57
import org.netbeans.libs.git.jgit.AbstractGitTestCase;
58
import org.netbeans.libs.git.jgit.Utils;
59
60
/**
61
 *
62
 * @author ondra
63
 */
64
public class CherryPickTest extends AbstractGitTestCase {
65
66
    private File workDir;
67
    private Repository repository;
68
    private static final String BRANCH = "b";
69
    
70
    public CherryPickTest (String testName) throws IOException {
71
        super(testName);
72
    }
73
74
    @Override
75
    protected void setUp() throws Exception {
76
        super.setUp();
77
        workDir = getWorkingDirectory();
78
        repository = getRepository(getLocalGitRepository());
79
    }
80
    
81
    public void testCherryPickCommit () throws Exception {
82
        File f = new File(workDir, "f");
83
        write(f, "init");
84
        add(f);
85
        commit(f);
86
        
87
        GitClient client = getClient(workDir);
88
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
89
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
90
        
91
        write(f, "change on branch");
92
        add(f);
93
        GitRevisionInfo c = client.commit(new File[] { f }, "on branch", null, null, NULL_PROGRESS_MONITOR);
94
        
95
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
96
        GitRevisionInfo initCommit = client.log("HEAD", NULL_PROGRESS_MONITOR);
97
        
98
        Thread.sleep(1100);
99
        
100
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN, new String[] { BRANCH }, NULL_PROGRESS_MONITOR);
101
        assertEquals(GitCherryPickResult.CherryPickStatus.OK, res.getCherryPickStatus());
102
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getParents()[0]);
103
        assertEquals(c.getCommitTime(), res.getCurrentHead().getCommitTime());
104
        assertEquals(1, res.getCherryPickedCommits().length);
105
        assertEquals(c.getRevision(), res.getCherryPickedCommits()[0].getRevision());
106
        assertFalse(new File(workDir, ".git/sequencer").exists());
107
    }
108
109
    public void testCherryPickCommits () throws Exception {
110
        File f = new File(workDir, "f");
111
        write(f, "init");
112
        add(f);
113
        commit(f);
114
        
115
        File[] roots = new File[] { f };
116
        
117
        GitClient client = getClient(workDir);
118
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
119
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
120
        
121
        write(f, "change 1 on branch");
122
        add(f);
123
        GitRevisionInfo c1 = client.commit(roots, "on branch 1", null, null, NULL_PROGRESS_MONITOR);
124
        write(f, "change 2 on branch");
125
        add(f);
126
        GitRevisionInfo c2 = client.commit(roots, "on branch 2", null, null, NULL_PROGRESS_MONITOR);
127
        
128
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
129
        GitRevisionInfo initCommit = client.log("HEAD", NULL_PROGRESS_MONITOR);
130
        
131
        Thread.sleep(1100);
132
        
133
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN,
134
                new String[] { c1.getRevision(), c2.getRevision() }, NULL_PROGRESS_MONITOR);
135
        assertEquals(GitCherryPickResult.CherryPickStatus.OK, res.getCherryPickStatus());
136
        SearchCriteria sc = new SearchCriteria();
137
        sc.setRevisionTo("HEAD");
138
        GitRevisionInfo[] logs = client.log(sc, NULL_PROGRESS_MONITOR);
139
        assertEquals(3, logs.length);
140
        assertEquals(c2.getFullMessage(), logs[0].getFullMessage());
141
        assertEquals(c2.getCommitTime(), logs[0].getCommitTime());
142
        assertEquals(c1.getFullMessage(), logs[1].getFullMessage());
143
        assertEquals(c1.getCommitTime(), logs[1].getCommitTime());
144
        assertEquals(initCommit.getRevision(), logs[2].getRevision());
145
146
        assertEquals(2, res.getCherryPickedCommits().length);
147
        assertEquals(c1.getRevision(), res.getCherryPickedCommits()[0].getRevision());
148
        assertEquals(c2.getRevision(), res.getCherryPickedCommits()[1].getRevision());
149
        assertFalse(new File(workDir, ".git/sequencer").exists());
150
    }
151
    
152
    public void testCherryPickFailure () throws Exception {
153
        File f = new File(workDir, "f");
154
        write(f, "init");
155
        add(f);
156
        commit(f);
157
        
158
        File[] roots = new File[] { f };
159
        
160
        GitClient client = getClient(workDir);
161
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
162
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
163
        
164
        write(f, "change on branch");
165
        add(f);
166
        GitRevisionInfo c = client.commit(roots, "on branch", null, null, NULL_PROGRESS_MONITOR);
167
        
168
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
169
        // make modification so cherry-pick cannot start
170
        write(f, "change in master");
171
        
172
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN, new String[] { BRANCH }, NULL_PROGRESS_MONITOR);
173
        assertEquals(GitCherryPickResult.CherryPickStatus.FAILED, res.getCherryPickStatus());
174
        assertEquals(0, res.getCurrentHead().getParents().length);
175
    }
176
    
177
    public void testCherryPickCommitsConflictAbort () throws Exception {
178
        File f = new File(workDir, "f");
179
        write(f, "init");
180
        add(f);
181
        commit(f);
182
        
183
        File[] roots = new File[] { f };
184
        
185
        GitClient client = getClient(workDir);
186
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
187
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
188
        
189
        write(f, "change 1 on branch");
190
        add(f);
191
        GitRevisionInfo c1 = client.commit(roots, "on branch 1", null, null, NULL_PROGRESS_MONITOR);
192
        write(f, "change 2 on branch");
193
        add(f);
194
        GitRevisionInfo c2 = client.commit(roots, "on branch 2", null, null, NULL_PROGRESS_MONITOR);
195
        write(f, "change 3 on branch");
196
        add(f);
197
        GitRevisionInfo c3 = client.commit(roots, "on branch 3", null, null, NULL_PROGRESS_MONITOR);
198
        
199
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
200
        GitRevisionInfo initCommit = client.log("HEAD", NULL_PROGRESS_MONITOR);
201
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN,
202
                new String[] { c1.getRevision(), c3.getRevision() }, NULL_PROGRESS_MONITOR);
203
        assertEquals(GitCherryPickResult.CherryPickStatus.CONFLICTING, res.getCherryPickStatus());
204
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getParents()[0]);
205
        
206
        res = client.cherryPick(GitClient.CherryPickOperation.ABORT, null, NULL_PROGRESS_MONITOR);
207
        assertEquals(GitCherryPickResult.CherryPickStatus.ABORTED, res.getCherryPickStatus());
208
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getRevision());
209
        assertStatus(client.getStatus(roots, NULL_PROGRESS_MONITOR), workDir, f, true,
210
                Status.STATUS_NORMAL, Status.STATUS_NORMAL, Status.STATUS_NORMAL, false);
211
    }
212
    
213
    public void testCherryPickCommitsConflictQuit () throws Exception {
214
        File f = new File(workDir, "f");
215
        write(f, "init");
216
        add(f);
217
        commit(f);
218
        
219
        File[] roots = new File[] { f };
220
        
221
        GitClient client = getClient(workDir);
222
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
223
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
224
        
225
        write(f, "change 1 on branch");
226
        add(f);
227
        GitRevisionInfo c1 = client.commit(roots, "on branch 1\nblablabla", null, null, NULL_PROGRESS_MONITOR);
228
        write(f, "change 2 on branch");
229
        add(f);
230
        GitRevisionInfo c2 = client.commit(roots, "on branch 2", null, null, NULL_PROGRESS_MONITOR);
231
        write(f, "change 3 on branch");
232
        add(f);
233
        GitRevisionInfo c3 = client.commit(roots, "on branch 3\nBLABLABLA", null, null, NULL_PROGRESS_MONITOR);
234
        
235
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
236
        GitRevisionInfo initCommit = client.log("HEAD", NULL_PROGRESS_MONITOR);
237
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN,
238
                new String[] { c1.getRevision(), c3.getRevision() }, NULL_PROGRESS_MONITOR);
239
        assertEquals(GitCherryPickResult.CherryPickStatus.CONFLICTING, res.getCherryPickStatus());
240
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getParents()[0]);
241
        
242
        ObjectReader or = repository.newObjectReader();
243
        RevCommit commit = Utils.findCommit(repository, c3.getRevision());
244
        assertEquals("pick " + or.abbreviate(commit).name() + " " + commit.getShortMessage(),
245
                read(new File(repository.getDirectory(), "sequencer/todo")));
246
        
247
        res = client.cherryPick(GitClient.CherryPickOperation.QUIT, null, NULL_PROGRESS_MONITOR);
248
        assertEquals(GitCherryPickResult.CherryPickStatus.CONFLICTING, res.getCherryPickStatus());
249
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getParents()[0]);
250
        assertStatus(client.getStatus(roots, NULL_PROGRESS_MONITOR), workDir, f, true,
251
                Status.STATUS_NORMAL, Status.STATUS_NORMAL, Status.STATUS_NORMAL, true);
252
        assertEquals(RepositoryState.CHERRY_PICKING, repository.getRepositoryState());
253
        
254
        res = client.cherryPick(GitClient.CherryPickOperation.ABORT, null, NULL_PROGRESS_MONITOR);
255
        assertEquals(GitCherryPickResult.CherryPickStatus.ABORTED, res.getCherryPickStatus());
256
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getParents()[0]);
257
        assertStatus(client.getStatus(roots, NULL_PROGRESS_MONITOR), workDir, f, true,
258
                Status.STATUS_NORMAL, Status.STATUS_NORMAL, Status.STATUS_NORMAL, false);
259
    }
260
    
261
    public void testCherryPickCommitConflictResolve () throws Exception {
262
        File f = new File(workDir, "f");
263
        write(f, "init");
264
        add(f);
265
        commit(f);
266
        
267
        File[] roots = new File[] { f };
268
        
269
        GitClient client = getClient(workDir);
270
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
271
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
272
        
273
        write(f, "change 1 on branch");
274
        add(f);
275
        GitRevisionInfo c1 = client.commit(roots, "on branch 1", null, null, NULL_PROGRESS_MONITOR);
276
        write(f, "change 2 on branch");
277
        add(f);
278
        GitRevisionInfo c2 = client.commit(roots, "on branch 2", null, null, NULL_PROGRESS_MONITOR);
279
        
280
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
281
        GitRevisionInfo initCommit = client.log("HEAD", NULL_PROGRESS_MONITOR);
282
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN,
283
                new String[] { c2.getRevision() }, NULL_PROGRESS_MONITOR);
284
        assertEquals(GitCherryPickResult.CherryPickStatus.CONFLICTING, res.getCherryPickStatus());
285
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getRevision());
286
        assertFalse(new File(repository.getDirectory(), "sequencer").exists());
287
        
288
        write(f, "init\nchange 2 on branch");
289
        add(f);
290
        
291
        // try continue, should interrupt and ask for commit
292
        res = client.cherryPick(GitClient.CherryPickOperation.CONTINUE, null, NULL_PROGRESS_MONITOR);
293
        assertEquals(GitCherryPickResult.CherryPickStatus.UNCOMMITTED, res.getCherryPickStatus());
294
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getRevision());
295
        assertFalse(new File(repository.getDirectory(), "sequencer").exists());
296
        
297
        GitRevisionInfo commit = client.commit(new File[0], c2.getFullMessage(), null, null, NULL_PROGRESS_MONITOR);
298
        assertEquals(c2.getCommitTime(), commit.getCommitTime());
299
        assertEquals(RepositoryState.SAFE, repository.getRepositoryState());
300
        assertFalse(new File(repository.getDirectory(), "sequencer").exists());
301
        
302
        res = client.cherryPick(GitClient.CherryPickOperation.CONTINUE, null, NULL_PROGRESS_MONITOR);
303
        assertEquals(GitCherryPickResult.CherryPickStatus.OK, res.getCherryPickStatus());
304
    }
305
    
306
    public void testCherryPickCommitsConflictResolve () throws Exception {
307
        File f = new File(workDir, "f");
308
        write(f, "init");
309
        add(f);
310
        commit(f);
311
        
312
        File[] roots = new File[] { f };
313
        
314
        GitClient client = getClient(workDir);
315
        client.createBranch(BRANCH, Constants.MASTER, NULL_PROGRESS_MONITOR);
316
        client.checkoutRevision(BRANCH, true, NULL_PROGRESS_MONITOR);
317
        
318
        write(f, "change 1 on branch");
319
        add(f);
320
        GitRevisionInfo c1 = client.commit(roots, "on branch 1", null, null, NULL_PROGRESS_MONITOR);
321
        write(f, "init\nchange 2 on branch");
322
        add(f);
323
        GitRevisionInfo c2 = client.commit(roots, "on branch 2", null, null, NULL_PROGRESS_MONITOR);
324
        write(f, "init\nchange 3 on branch");
325
        add(f);
326
        GitRevisionInfo c3 = client.commit(roots, "on branch 3", null, null, NULL_PROGRESS_MONITOR);
327
        
328
        Thread.sleep(1100);
329
        
330
        client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR);
331
        GitRevisionInfo initCommit = client.log("HEAD", NULL_PROGRESS_MONITOR);
332
        GitCherryPickResult res = client.cherryPick(GitClient.CherryPickOperation.BEGIN,
333
                new String[] { c2.getRevision(), c3.getRevision() }, NULL_PROGRESS_MONITOR);
334
        assertEquals(GitCherryPickResult.CherryPickStatus.CONFLICTING, res.getCherryPickStatus());
335
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getRevision());
336
        
337
        write(f, "init\nchange 2 on branch");
338
        add(f);
339
        
340
        // try continue, should interrupt and ask for commit
341
        res = client.cherryPick(GitClient.CherryPickOperation.CONTINUE, null, NULL_PROGRESS_MONITOR);
342
        assertEquals(GitCherryPickResult.CherryPickStatus.UNCOMMITTED, res.getCherryPickStatus());
343
        assertEquals(initCommit.getRevision(), res.getCurrentHead().getRevision());
344
        assertTrue(new File(repository.getDirectory(), "sequencer").exists());
345
        
346
        GitRevisionInfo commit = client.commit(new File[0], c2.getFullMessage(), null, null, NULL_PROGRESS_MONITOR);
347
        assertEquals(c2.getCommitTime(), commit.getCommitTime());
348
        assertEquals(RepositoryState.SAFE, repository.getRepositoryState());
349
        assertTrue(new File(repository.getDirectory(), "sequencer").exists());
350
        
351
        res = client.cherryPick(GitClient.CherryPickOperation.CONTINUE, null, NULL_PROGRESS_MONITOR);
352
        assertEquals(GitCherryPickResult.CherryPickStatus.OK, res.getCherryPickStatus());
353
        assertEquals(commit.getRevision(), res.getCurrentHead().getParents()[0]);
354
        assertEquals(c3.getCommitTime(), res.getCurrentHead().getCommitTime());
355
        assertFalse(new File(repository.getDirectory(), "sequencer").exists());
356
        assertEquals(1, res.getCherryPickedCommits().length);
357
        assertEquals(c3.getRevision(), res.getCherryPickedCommits()[0].getRevision());
358
    }
359
}
(-)a/libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/CommitTest.java (+39 lines)
Lines 679-682 Link Here
679
        assertEquals(info.getRevision(), lastCommit.getParents()[0]);
679
        assertEquals(info.getRevision(), lastCommit.getParents()[0]);
680
        assertEquals(lastCommit.getRevision(), client.getBranches(false, NULL_PROGRESS_MONITOR).get("master").getId());
680
        assertEquals(lastCommit.getRevision(), client.getBranches(false, NULL_PROGRESS_MONITOR).get("master").getId());
681
    }
681
    }
682
    
683
    public void testCherryPickCommit () throws Exception {
684
        repository.getConfig().setString("user", null, "name", "John");
685
        repository.getConfig().setString("user", null, "email", "john@git.com");
686
        repository.getConfig().save();
687
        
688
        File f = new File(workDir, "f");
689
        write(f, "init");
690
        File[] files = new File[] { f };
691
        
692
        add(f);
693
        commit(f);
694
        
695
        GitClient client = getClient(workDir);
696
        write(f, "change");
697
        add(f);
698
        GitRevisionInfo info = client.commit(files, "change to CherryPick", null, null, NULL_PROGRESS_MONITOR);
699
        
700
        Thread.sleep(1100);
701
        
702
        client.reset("HEAD~1", GitClient.ResetType.MIXED, NULL_PROGRESS_MONITOR);
703
        repository.writeCherryPickHead(repository.resolve(info.getRevision()));
704
        
705
        // now we are cherry-picking
706
        // amend is not allowed
707
        try {
708
            client.commit(new File[0], info.getFullMessage(), null, null, true, NULL_PROGRESS_MONITOR);
709
            fail("Amend not allowed");
710
        } catch (GitException ex) {
711
            assertEquals(Utils.getBundle(CommitCommand.class).getString("MSG_Error_Commit_CannotAmend"), ex.getMessage());
712
        }
713
        
714
        // doing commit should preserve authorship of the original commit (info)
715
        GitRevisionInfo commit = client.commit(new File[0], info.getFullMessage(), null, null, NULL_PROGRESS_MONITOR);
716
        assertEquals(info.getAuthor(), commit.getAuthor());
717
        assertEquals(info.getCommitTime(), commit.getCommitTime());
718
        assertEquals(Utils.findCommit(repository, info.getRevision()).getAuthorIdent().getWhen(),
719
                Utils.findCommit(repository, commit.getRevision()).getAuthorIdent().getWhen());
720
    }
682
}
721
}

Return to bug 235267