2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
using System.Collections.Generic;
49
using NGit.Api.Errors;
54
using NGit.Treewalk.Filter;
61
/// A class used to execute a
62
/// <code>Rebase</code>
63
/// command. It has setters for all
64
/// supported options and arguments of this command and a
65
/// <see cref="Call()">Call()</see>
67
/// to finally execute the command. Each instance of this class should only be
68
/// used for one invocation of the command (means: one call to
69
/// <see cref="Call()">Call()</see>
74
/// * href="http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html"
75
/// * >Git documentation about Rebase</a></seealso>
76
public class RebaseCommand : GitCommand<RebaseResult>
78
/// <summary>The name of the "rebase-merge" folder</summary>
79
public static readonly string REBASE_MERGE = "rebase-merge";
81
/// <summary>The name of the "stopped-sha" file</summary>
82
public static readonly string STOPPED_SHA = "stopped-sha";
84
private static readonly string AUTHOR_SCRIPT = "author-script";
86
private static readonly string DONE = "done";
88
private static readonly string GIT_AUTHOR_DATE = "GIT_AUTHOR_DATE";
90
private static readonly string GIT_AUTHOR_EMAIL = "GIT_AUTHOR_EMAIL";
92
private static readonly string GIT_AUTHOR_NAME = "GIT_AUTHOR_NAME";
94
private static readonly string GIT_REBASE_TODO = "git-rebase-todo";
96
private static readonly string HEAD_NAME = "head-name";
98
private static readonly string INTERACTIVE = "interactive";
100
private static readonly string MESSAGE = "message";
102
private static readonly string ONTO = "onto";
104
private static readonly string PATCH = "patch";
106
private static readonly string REBASE_HEAD = "head";
108
/// <summary>The available operations</summary>
109
public enum Operation
117
private RebaseCommand.Operation operation = RebaseCommand.Operation.BEGIN;
119
private RevCommit upstreamCommit;
121
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
123
private readonly RevWalk walk;
125
private readonly FilePath rebaseDir;
127
/// <param name="repo"></param>
128
protected internal RebaseCommand(Repository repo) : base(repo)
130
walk = new RevWalk(repo);
131
rebaseDir = new FilePath(repo.Directory, REBASE_MERGE);
136
/// <code>Rebase</code>
137
/// command with all the options and parameters
138
/// collected by the setter methods of this class. Each instance of this
139
/// class should only be used for one invocation of the command. Don't call
140
/// this method twice on an instance.
142
/// <returns>an object describing the result of this command</returns>
143
/// <exception cref="NGit.Api.Errors.NoHeadException"></exception>
144
/// <exception cref="NGit.Api.Errors.RefNotFoundException"></exception>
145
/// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
146
/// <exception cref="NGit.Api.Errors.GitAPIException"></exception>
147
public override RebaseResult Call()
149
RevCommit newHead = null;
150
bool lastStepWasForward = false;
157
case RebaseCommand.Operation.ABORT:
161
return Abort(new RebaseResult(RebaseResult.Status.ABORTED));
163
catch (IOException ioe)
165
throw new JGitInternalException(ioe.Message, ioe);
167
goto case RebaseCommand.Operation.SKIP;
170
case RebaseCommand.Operation.SKIP:
171
case RebaseCommand.Operation.CONTINUE:
174
string upstreamCommitName = ReadFile(rebaseDir, ONTO);
175
this.upstreamCommit = walk.ParseCommit(repo.Resolve(upstreamCommitName));
179
case RebaseCommand.Operation.BEGIN:
181
RebaseResult res = InitFilesAndRewind();
189
if (monitor.IsCancelled())
191
return Abort(new RebaseResult(RebaseResult.Status.ABORTED));
193
if (operation == RebaseCommand.Operation.CONTINUE)
195
newHead = ContinueRebase();
197
if (operation == RebaseCommand.Operation.SKIP)
199
newHead = CheckoutCurrentHead();
201
ObjectReader or = repo.NewObjectReader();
202
IList<RebaseCommand.Step> steps = LoadSteps();
203
foreach (RebaseCommand.Step step in steps)
206
ICollection<ObjectId> ids = or.Resolve(step.commit);
209
throw new JGitInternalException("Could not resolve uniquely the abbreviated object ID"
212
RevCommit commitToPick = walk.ParseCommit(ids.Iterator().Next());
213
if (monitor.IsCancelled())
215
return new RebaseResult(commitToPick);
219
monitor.BeginTask(MessageFormat.Format(JGitText.Get().applyingCommit, commitToPick
220
.GetShortMessage()), ProgressMonitor.UNKNOWN);
221
// if the first parent of commitToPick is the current HEAD,
222
// we do a fast-forward instead of cherry-pick to avoid
223
// unnecessary object rewriting
224
newHead = TryFastForward(commitToPick);
225
lastStepWasForward = newHead != null;
226
if (!lastStepWasForward)
228
// TODO if the content of this commit is already merged
229
// here we should skip this step in order to avoid
230
// confusing pseudo-changed
231
CherryPickResult cherryPickResult = new Git(repo).CherryPick().Include(commitToPick
233
switch (cherryPickResult.GetStatus())
235
case CherryPickResult.CherryPickStatus.FAILED:
237
if (operation == RebaseCommand.Operation.BEGIN)
239
return Abort(new RebaseResult(cherryPickResult.GetFailingPaths()));
243
return Stop(commitToPick);
245
goto case CherryPickResult.CherryPickStatus.CONFLICTING;
248
case CherryPickResult.CherryPickStatus.CONFLICTING:
250
return Stop(commitToPick);
253
case CherryPickResult.CherryPickStatus.OK:
255
newHead = cherryPickResult.GetNewHead();
268
// point the previous head (if any) to the new commit
269
string headName = ReadFile(rebaseDir, HEAD_NAME);
270
if (headName.StartsWith(Constants.R_REFS))
272
RefUpdate rup = repo.UpdateRef(headName);
273
rup.SetNewObjectId(newHead);
274
RefUpdate.Result res_1 = rup.ForceUpdate();
277
case RefUpdate.Result.FAST_FORWARD:
278
case RefUpdate.Result.FORCED:
279
case RefUpdate.Result.NO_CHANGE:
286
throw new JGitInternalException("Updating HEAD failed");
289
rup = repo.UpdateRef(Constants.HEAD);
290
res_1 = rup.Link(headName);
293
case RefUpdate.Result.FAST_FORWARD:
294
case RefUpdate.Result.FORCED:
295
case RefUpdate.Result.NO_CHANGE:
302
throw new JGitInternalException("Updating HEAD failed");
306
FileUtils.Delete(rebaseDir, FileUtils.RECURSIVE);
307
if (lastStepWasForward)
309
return new RebaseResult(RebaseResult.Status.FAST_FORWARD);
311
return new RebaseResult(RebaseResult.Status.OK);
313
return new RebaseResult(RebaseResult.Status.UP_TO_DATE);
315
catch (IOException ioe)
317
throw new JGitInternalException(ioe.Message, ioe);
321
/// <exception cref="System.IO.IOException"></exception>
322
/// <exception cref="NGit.Api.Errors.NoHeadException"></exception>
323
/// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
324
private RevCommit CheckoutCurrentHead()
326
ObjectId headTree = repo.Resolve(Constants.HEAD + "^{tree}");
327
if (headTree == null)
329
throw new NoHeadException(JGitText.Get().cannotRebaseWithoutCurrentHead);
331
DirCache dc = repo.LockDirCache();
334
DirCacheCheckout dco = new DirCacheCheckout(repo, dc, headTree);
335
dco.SetFailOnConflict(false);
336
bool needsDeleteFiles = dco.Checkout();
337
if (needsDeleteFiles)
339
IList<string> fileList = dco.GetToBeDeleted();
340
foreach (string filePath in fileList)
342
FilePath fileToDelete = new FilePath(repo.WorkTree, filePath);
343
if (fileToDelete.Exists())
345
FileUtils.Delete(fileToDelete, FileUtils.RECURSIVE | FileUtils.RETRY);
354
RevWalk rw = new RevWalk(repo);
355
RevCommit commit = rw.ParseCommit(repo.Resolve(Constants.HEAD));
360
/// <returns>the commit if we had to do a commit, otherwise null</returns>
361
/// <exception cref="NGit.Api.Errors.GitAPIException">NGit.Api.Errors.GitAPIException
363
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
364
private RevCommit ContinueRebase()
366
// if there are still conflicts, we throw a specific Exception
367
DirCache dc = repo.ReadDirCache();
368
bool hasUnmergedPaths = dc.HasUnmergedPaths();
369
if (hasUnmergedPaths)
371
throw new UnmergedPathsException();
373
// determine whether we need to commit
374
TreeWalk treeWalk = new TreeWalk(repo);
376
treeWalk.Recursive = true;
377
treeWalk.AddTree(new DirCacheIterator(dc));
378
ObjectId id = repo.Resolve(Constants.HEAD + "^{tree}");
381
throw new NoHeadException(JGitText.Get().cannotRebaseWithoutCurrentHead);
383
treeWalk.AddTree(id);
384
treeWalk.Filter = TreeFilter.ANY_DIFF;
385
bool needsCommit = treeWalk.Next();
389
CommitCommand commit = new Git(repo).Commit();
390
commit.SetMessage(ReadFile(rebaseDir, MESSAGE));
391
commit.SetAuthor(ParseAuthor());
392
return commit.Call();
397
/// <exception cref="System.IO.IOException"></exception>
398
private PersonIdent ParseAuthor()
400
FilePath authorScriptFile = new FilePath(rebaseDir, AUTHOR_SCRIPT);
404
raw = IOUtil.ReadFully(authorScriptFile);
406
catch (FileNotFoundException)
410
return ParseAuthor(raw);
413
/// <exception cref="System.IO.IOException"></exception>
414
private RebaseResult Stop(RevCommit commitToPick)
416
PersonIdent author = commitToPick.GetAuthorIdent();
417
string authorScript = ToAuthorScript(author);
418
CreateFile(rebaseDir, AUTHOR_SCRIPT, authorScript);
419
CreateFile(rebaseDir, MESSAGE, commitToPick.GetFullMessage());
420
ByteArrayOutputStream bos = new ByteArrayOutputStream();
421
DiffFormatter df = new DiffFormatter(bos);
422
df.SetRepository(repo);
423
df.Format(commitToPick.GetParent(0), commitToPick);
424
CreateFile(rebaseDir, PATCH, Sharpen.Runtime.GetStringForBytes(bos.ToByteArray(),
425
Constants.CHARACTER_ENCODING));
426
CreateFile(rebaseDir, STOPPED_SHA, repo.NewObjectReader().Abbreviate(commitToPick
428
return new RebaseResult(commitToPick);
431
internal virtual string ToAuthorScript(PersonIdent author)
433
StringBuilder sb = new StringBuilder(100);
434
sb.Append(GIT_AUTHOR_NAME);
436
sb.Append(author.GetName());
438
sb.Append(GIT_AUTHOR_EMAIL);
440
sb.Append(author.GetEmailAddress());
442
// the command line uses the "external String"
443
// representation for date and timezone
444
sb.Append(GIT_AUTHOR_DATE);
446
string externalString = author.ToExternalString();
447
sb.Append(Sharpen.Runtime.Substring(externalString, externalString.LastIndexOf('>'
450
return sb.ToString();
454
/// Removes the number of lines given in the parameter from the
455
/// <code>git-rebase-todo</code> file but preserves comments and other lines
456
/// that can not be parsed as steps
458
/// <param name="numSteps"></param>
459
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
460
private void PopSteps(int numSteps)
466
IList<string> todoLines = new AList<string>();
467
IList<string> poppedLines = new AList<string>();
468
FilePath todoFile = new FilePath(rebaseDir, GIT_REBASE_TODO);
469
FilePath doneFile = new FilePath(rebaseDir, DONE);
470
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(
471
todoFile), Constants.CHARACTER_ENCODING));
474
// check if the line starts with a action tag (pick, skip...)
475
while (poppedLines.Count < numSteps)
477
string popCandidate = br.ReadLine();
478
if (popCandidate == null)
482
if (popCandidate[0] == '#')
486
int spaceIndex = popCandidate.IndexOf(' ');
490
string actionToken = Sharpen.Runtime.Substring(popCandidate, 0, spaceIndex);
491
pop = RebaseCommand.Action.Parse(actionToken) != null;
495
poppedLines.AddItem(popCandidate);
499
todoLines.AddItem(popCandidate);
502
string readLine = br.ReadLine();
503
while (readLine != null)
505
todoLines.AddItem(readLine);
506
readLine = br.ReadLine();
513
BufferedWriter todoWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream
514
(todoFile), Constants.CHARACTER_ENCODING));
517
foreach (string writeLine in todoLines)
519
todoWriter.Write(writeLine);
520
todoWriter.NewLine();
527
if (poppedLines.Count > 0)
530
BufferedWriter doneWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream
531
(doneFile, true), Constants.CHARACTER_ENCODING));
534
foreach (string writeLine in poppedLines)
536
doneWriter.Write(writeLine);
537
doneWriter.NewLine();
547
/// <exception cref="NGit.Api.Errors.RefNotFoundException"></exception>
548
/// <exception cref="System.IO.IOException"></exception>
549
/// <exception cref="NGit.Api.Errors.NoHeadException"></exception>
550
/// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
551
private RebaseResult InitFilesAndRewind()
553
// we need to store everything into files so that we can implement
554
// --skip, --continue, and --abort
555
// first of all, we determine the commits to be applied
556
IList<RevCommit> cherryPickList = new AList<RevCommit>();
557
Ref head = repo.GetRef(Constants.HEAD);
558
if (head == null || head.GetObjectId() == null)
560
throw new RefNotFoundException(MessageFormat.Format(JGitText.Get().refNotResolved
564
if (head.IsSymbolic())
566
headName = head.GetTarget().GetName();
570
headName = "detached HEAD";
572
ObjectId headId = head.GetObjectId();
575
throw new RefNotFoundException(MessageFormat.Format(JGitText.Get().refNotResolved
578
RevCommit headCommit = walk.LookupCommit(headId);
579
monitor.BeginTask(JGitText.Get().obtainingCommitsForCherryPick, ProgressMonitor.UNKNOWN
581
LogCommand cmd = new Git(repo).Log().AddRange(upstreamCommit, headCommit);
582
Iterable<RevCommit> commitsToUse = cmd.Call();
583
foreach (RevCommit commit in commitsToUse)
585
cherryPickList.AddItem(commit);
587
// if the upstream commit is in a direct line to the current head,
588
// the log command will not report any commits; in this case,
589
// we create the cherry-pick list ourselves
590
if (cherryPickList.IsEmpty())
592
Iterable<RevCommit> parents = new Git(repo).Log().Add(upstreamCommit).Call();
593
foreach (RevCommit parent in parents)
595
if (parent.Equals(headCommit))
599
if (parent.ParentCount != 1)
601
throw new JGitInternalException(JGitText.Get().canOnlyCherryPickCommitsWithOneParent
604
cherryPickList.AddItem(parent);
607
// nothing to do: return with UP_TO_DATE_RESULT
608
if (cherryPickList.IsEmpty())
610
return RebaseResult.UP_TO_DATE_RESULT;
612
Sharpen.Collections.Reverse(cherryPickList);
613
// create the folder for the meta information
614
FileUtils.Mkdir(rebaseDir);
615
CreateFile(repo.Directory, Constants.ORIG_HEAD, headId.Name);
616
CreateFile(rebaseDir, REBASE_HEAD, headId.Name);
617
CreateFile(rebaseDir, HEAD_NAME, headName);
618
CreateFile(rebaseDir, ONTO, upstreamCommit.Name);
619
CreateFile(rebaseDir, INTERACTIVE, string.Empty);
620
BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream
621
(new FilePath(rebaseDir, GIT_REBASE_TODO)), Constants.CHARACTER_ENCODING));
622
fw.Write("# Created by EGit: rebasing " + upstreamCommit.Name + " onto " + headId
627
StringBuilder sb = new StringBuilder();
628
ObjectReader reader = walk.GetObjectReader();
629
foreach (RevCommit commit_1 in cherryPickList)
632
sb.Append(RebaseCommand.Action.PICK.ToToken());
634
sb.Append(reader.Abbreviate(commit_1).Name);
636
sb.Append(commit_1.GetShortMessage());
637
fw.Write(sb.ToString());
646
// we rewind to the upstream commit
647
monitor.BeginTask(MessageFormat.Format(JGitText.Get().rewinding, upstreamCommit.GetShortMessage
648
()), ProgressMonitor.UNKNOWN);
649
CheckoutCommit(upstreamCommit);
654
/// <summary>checks if we can fast-forward and returns the new head if it is possible
656
/// <param name="newCommit"></param>
657
/// <returns>the new head, or null</returns>
658
/// <exception cref="NGit.Api.Errors.RefNotFoundException">NGit.Api.Errors.RefNotFoundException
660
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
661
public virtual RevCommit TryFastForward(RevCommit newCommit)
663
Ref head = repo.GetRef(Constants.HEAD);
664
if (head == null || head.GetObjectId() == null)
666
throw new RefNotFoundException(MessageFormat.Format(JGitText.Get().refNotResolved
669
ObjectId headId = head.GetObjectId();
672
throw new RefNotFoundException(MessageFormat.Format(JGitText.Get().refNotResolved
675
RevCommit headCommit = walk.LookupCommit(headId);
676
if (walk.IsMergedInto(newCommit, headCommit))
681
if (head.IsSymbolic())
683
headName = head.GetTarget().GetName();
687
headName = "detached HEAD";
689
return TryFastForward(headName, headCommit, newCommit);
692
/// <exception cref="System.IO.IOException"></exception>
693
/// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
694
private RevCommit TryFastForward(string headName, RevCommit oldCommit, RevCommit
697
bool tryRebase = false;
698
foreach (RevCommit parentCommit in newCommit.Parents)
700
if (parentCommit.Equals(oldCommit))
709
CheckoutCommand co = new CheckoutCommand(repo);
712
co.SetName(newCommit.Name).Call();
713
if (headName.StartsWith(Constants.R_HEADS))
715
RefUpdate rup = repo.UpdateRef(headName);
716
rup.SetExpectedOldObjectId(oldCommit);
717
rup.SetNewObjectId(newCommit);
718
rup.SetRefLogMessage("Fast-foward from " + oldCommit.Name + " to " + newCommit.Name
720
RefUpdate.Result res = rup.Update(walk);
723
case RefUpdate.Result.FAST_FORWARD:
724
case RefUpdate.Result.NO_CHANGE:
725
case RefUpdate.Result.FORCED:
732
throw new IOException("Could not fast-forward");
738
catch (RefAlreadyExistsException e)
740
throw new JGitInternalException(e.Message, e);
742
catch (RefNotFoundException e)
744
throw new JGitInternalException(e.Message, e);
746
catch (InvalidRefNameException e)
748
throw new JGitInternalException(e.Message, e);
752
/// <exception cref="NGit.Api.Errors.WrongRepositoryStateException"></exception>
753
private void CheckParameters()
755
RepositoryState s = repo.GetRepositoryState();
756
if (this.operation != RebaseCommand.Operation.BEGIN)
758
// these operations are only possible while in a rebasing state
759
if (repo.GetRepositoryState() != RepositoryState.REBASING_INTERACTIVE)
761
throw new WrongRepositoryStateException(MessageFormat.Format(JGitText.Get().wrongRepositoryState
762
, repo.GetRepositoryState().Name()));
767
if (s == RepositoryState.SAFE)
769
if (this.upstreamCommit == null)
771
throw new JGitInternalException(MessageFormat.Format(JGitText.Get().missingRequiredParameter
778
throw new WrongRepositoryStateException(MessageFormat.Format(JGitText.Get().wrongRepositoryState
779
, repo.GetRepositoryState().Name()));
784
/// <exception cref="System.IO.IOException"></exception>
785
private void CreateFile(FilePath parentDir, string name, string content)
787
FilePath file = new FilePath(parentDir, name);
788
FileOutputStream fos = new FileOutputStream(file);
791
fos.Write(Sharpen.Runtime.GetBytesForString(content, Constants.CHARACTER_ENCODING
801
/// <exception cref="System.IO.IOException"></exception>
802
private RebaseResult Abort(RebaseResult result)
806
string commitId = ReadFile(repo.Directory, Constants.ORIG_HEAD);
807
monitor.BeginTask(MessageFormat.Format(JGitText.Get().abortingRebase, commitId),
808
ProgressMonitor.UNKNOWN);
809
DirCacheCheckout dco;
810
RevCommit commit = walk.ParseCommit(repo.Resolve(commitId));
811
if (result.GetStatus().Equals(RebaseResult.Status.FAILED))
813
RevCommit head = walk.ParseCommit(repo.Resolve(Constants.HEAD));
814
dco = new DirCacheCheckout(repo, head.Tree, repo.LockDirCache(), commit.Tree);
818
dco = new DirCacheCheckout(repo, repo.LockDirCache(), commit.Tree);
820
dco.SetFailOnConflict(false);
830
string headName = ReadFile(rebaseDir, HEAD_NAME);
831
if (headName.StartsWith(Constants.R_REFS))
833
monitor.BeginTask(MessageFormat.Format(JGitText.Get().resettingHead, headName), ProgressMonitor
836
RefUpdate refUpdate = repo.UpdateRef(Constants.HEAD, false);
837
RefUpdate.Result res = refUpdate.Link(headName);
840
case RefUpdate.Result.FAST_FORWARD:
841
case RefUpdate.Result.FORCED:
842
case RefUpdate.Result.NO_CHANGE:
849
throw new JGitInternalException(JGitText.Get().abortingRebaseFailed);
854
FileUtils.Delete(rebaseDir, FileUtils.RECURSIVE);
863
/// <exception cref="System.IO.IOException"></exception>
864
private string ReadFile(FilePath directory, string fileName)
866
byte[] content = IOUtil.ReadFully(new FilePath(directory, fileName));
867
// strip off the last LF
868
int end = content.Length;
869
while (0 < end && content[end - 1] == '\n')
873
return RawParseUtils.Decode(content, 0, end);
876
/// <exception cref="System.IO.IOException"></exception>
877
private void CheckoutCommit(RevCommit commit)
881
RevCommit head = walk.ParseCommit(repo.Resolve(Constants.HEAD));
882
DirCacheCheckout dco = new DirCacheCheckout(repo, head.Tree, repo.LockDirCache(),
884
dco.SetFailOnConflict(true);
887
RefUpdate refUpdate = repo.UpdateRef(Constants.HEAD, true);
888
refUpdate.SetExpectedOldObjectId(head);
889
refUpdate.SetNewObjectId(commit);
890
RefUpdate.Result res = refUpdate.ForceUpdate();
893
case RefUpdate.Result.FAST_FORWARD:
894
case RefUpdate.Result.NO_CHANGE:
895
case RefUpdate.Result.FORCED:
902
throw new IOException("Could not rewind to upstream commit");
913
/// <exception cref="System.IO.IOException"></exception>
914
private IList<RebaseCommand.Step> LoadSteps()
916
byte[] buf = IOUtil.ReadFully(new FilePath(rebaseDir, GIT_REBASE_TODO));
919
AList<RebaseCommand.Step> r = new AList<RebaseCommand.Step>();
920
while (ptr < buf.Length)
923
ptr = RawParseUtils.NextLF(buf, ptr);
926
RebaseCommand.Step current = null;
927
while (tokenCount < 3 && nextSpace < ptr)
933
nextSpace = RawParseUtils.Next(buf, tokenBegin, ' ');
934
string actionToken = Sharpen.Runtime.GetStringForBytes(buf, tokenBegin, nextSpace
936
tokenBegin = nextSpace;
937
if (actionToken[0] == '#')
942
RebaseCommand.Action action = RebaseCommand.Action.Parse(actionToken);
945
current = new RebaseCommand.Step(RebaseCommand.Action.Parse(actionToken));
956
nextSpace = RawParseUtils.Next(buf, tokenBegin, ' ');
957
string commitToken = Sharpen.Runtime.GetStringForBytes(buf, tokenBegin, nextSpace
959
tokenBegin = nextSpace;
960
current.commit = AbbreviatedObjectId.FromString(commitToken);
971
int length = ptr - tokenBegin;
972
current.shortMessage = new byte[length];
973
System.Array.Copy(buf, tokenBegin, current.shortMessage, 0, length);
984
/// <param name="upstream">the upstream commit</param>
987
/// <code>this</code>
989
public virtual NGit.Api.RebaseCommand SetUpstream(RevCommit upstream)
991
this.upstreamCommit = upstream;
995
/// <param name="upstream">id of the upstream commit</param>
998
/// <code>this</code>
1000
public virtual NGit.Api.RebaseCommand SetUpstream(AnyObjectId upstream)
1004
this.upstreamCommit = walk.ParseCommit(upstream);
1006
catch (IOException e)
1008
throw new JGitInternalException(MessageFormat.Format(JGitText.Get().couldNotReadObjectWhileParsingCommit
1009
, upstream.Name), e);
1014
/// <param name="upstream">the upstream branch</param>
1017
/// <code>this</code>
1019
/// <exception cref="NGit.Api.Errors.RefNotFoundException">NGit.Api.Errors.RefNotFoundException
1021
public virtual NGit.Api.RebaseCommand SetUpstream(string upstream)
1025
ObjectId upstreamId = repo.Resolve(upstream);
1026
if (upstreamId == null)
1028
throw new RefNotFoundException(MessageFormat.Format(JGitText.Get().refNotResolved
1031
upstreamCommit = walk.ParseCommit(repo.Resolve(upstream));
1034
catch (IOException ioe)
1036
throw new JGitInternalException(ioe.Message, ioe);
1040
/// <param name="operation">the operation to perform</param>
1043
/// <code>this</code>
1045
public virtual NGit.Api.RebaseCommand SetOperation(RebaseCommand.Operation operation
1048
this.operation = operation;
1052
/// <param name="monitor">a progress monitor</param>
1053
/// <returns>this instance</returns>
1054
public virtual NGit.Api.RebaseCommand SetProgressMonitor(ProgressMonitor monitor)
1056
this.monitor = monitor;
1060
internal class Action
1062
public static RebaseCommand.Action PICK = new RebaseCommand.Action("pick");
1064
public readonly string token;
1066
private Action(string token)
1068
// later add SQUASH, EDIT, etc.
1072
public virtual string ToToken()
1077
internal static RebaseCommand.Action Parse(string token)
1079
if (token.Equals("pick") || token.Equals("p"))
1083
throw new JGitInternalException(MessageFormat.Format("Unknown or unsupported command \"{0}\", only \"pick\" is allowed"
1090
internal RebaseCommand.Action action;
1092
internal AbbreviatedObjectId commit;
1094
internal byte[] shortMessage;
1096
internal Step(RebaseCommand.Action action)
1098
this.action = action;
1102
internal virtual PersonIdent ParseAuthor(byte[] raw)
1104
if (raw.Length == 0)
1108
IDictionary<string, string> keyValueMap = new Dictionary<string, string>();
1109
for (int p = 0; p < raw.Length; )
1111
int end = RawParseUtils.NextLF(raw, p);
1116
int equalsIndex = RawParseUtils.Next(raw, p, '=');
1117
if (equalsIndex == end)
1121
string key = RawParseUtils.Decode(raw, p, equalsIndex - 1);
1122
string value = RawParseUtils.Decode(raw, equalsIndex + 1, end - 2);
1124
keyValueMap.Put(key, value);
1126
string name = keyValueMap.Get(GIT_AUTHOR_NAME);
1127
string email = keyValueMap.Get(GIT_AUTHOR_EMAIL);
1128
string time = keyValueMap.Get(GIT_AUTHOR_DATE);
1129
// the time is saved as <seconds since 1970> <timezone offset>
1130
long when = long.Parse(Sharpen.Runtime.Substring(time, 0, time.IndexOf(' '))) * 1000;
1131
string tzOffsetString = Sharpen.Runtime.Substring(time, time.IndexOf(' ') + 1);
1132
int multiplier = -1;
1133
if (tzOffsetString[0] == '+')
1137
int hours = System.Convert.ToInt32(Sharpen.Runtime.Substring(tzOffsetString, 1, 3
1139
int minutes = System.Convert.ToInt32(Sharpen.Runtime.Substring(tzOffsetString, 3,
1141
// this is in format (+/-)HHMM (hours and minutes)
1142
// we need to convert into minutes
1143
int tz = (hours * 60 + minutes) * multiplier;
1144
if (name != null && email != null)
1146
return new PersonIdent(name, email, when, tz);