~ubuntu-branches/ubuntu/saucy/monodevelop/saucy

« back to all changes in this revision

Viewing changes to external/ngit/NGit/NGit.Api/StashApplyCommand.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2012-05-27 18:08:20 UTC
  • mfrom: (1.8.5) (1.5.8 sid)
  • Revision ID: package-import@ubuntu.com-20120527180820-f1ub6lhg0s50wci1
Tags: 3.0.2+dfsg-3
* [fcecfe7] Fix monodevelop-core-addins.pc.in to point to actual 
  installed location of assemblies.
* [26e1a07] DebSrc 3.0 does not support Quilt's -p parameter, so 
  manually adjust the path in the patch file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
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
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
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.
 
23
 
 
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
 
27
  written permission.
 
28
 
 
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.
 
42
*/
 
43
 
 
44
using System.IO;
 
45
using NGit;
 
46
using NGit.Api;
 
47
using NGit.Api.Errors;
 
48
using NGit.Dircache;
 
49
using NGit.Internal;
 
50
using NGit.Revwalk;
 
51
using NGit.Treewalk;
 
52
using NGit.Treewalk.Filter;
 
53
using NGit.Util;
 
54
using Sharpen;
 
55
 
 
56
namespace NGit.Api
 
57
{
 
58
        /// <summary>Command class to apply a stashed commit.</summary>
 
59
        /// <remarks>Command class to apply a stashed commit.</remarks>
 
60
        /// <seealso><a href="http://www.kernel.org/pub/software/scm/git/docs/git-stash.html"
 
61
        /// *      >Git documentation about Stash</a></seealso>
 
62
        /// <since>2.0</since>
 
63
        public class StashApplyCommand : GitCommand<ObjectId>
 
64
        {
 
65
                private static readonly string DEFAULT_REF = Constants.STASH + "@{0}";
 
66
 
 
67
                /// <summary>
 
68
                /// Stash diff filter that looks for differences in the first three trees
 
69
                /// which must be the stash head tree, stash index tree, and stash working
 
70
                /// directory tree in any order.
 
71
                /// </summary>
 
72
                /// <remarks>
 
73
                /// Stash diff filter that looks for differences in the first three trees
 
74
                /// which must be the stash head tree, stash index tree, and stash working
 
75
                /// directory tree in any order.
 
76
                /// </remarks>
 
77
                private class StashDiffFilter : TreeFilter
 
78
                {
 
79
                        public override bool Include(TreeWalk walker)
 
80
                        {
 
81
                                int m = walker.GetRawMode(0);
 
82
                                if (walker.GetRawMode(1) != m || !walker.IdEqual(1, 0))
 
83
                                {
 
84
                                        return true;
 
85
                                }
 
86
                                if (walker.GetRawMode(2) != m || !walker.IdEqual(2, 0))
 
87
                                {
 
88
                                        return true;
 
89
                                }
 
90
                                return false;
 
91
                        }
 
92
 
 
93
                        public override bool ShouldBeRecursive()
 
94
                        {
 
95
                                return false;
 
96
                        }
 
97
 
 
98
                        public override TreeFilter Clone()
 
99
                        {
 
100
                                return this;
 
101
                        }
 
102
 
 
103
                        public override string ToString()
 
104
                        {
 
105
                                return "STASH_DIFF";
 
106
                        }
 
107
                }
 
108
 
 
109
                private string stashRef;
 
110
 
 
111
                /// <summary>Create command to apply the changes of a stashed commit</summary>
 
112
                /// <param name="repo"></param>
 
113
                protected internal StashApplyCommand(Repository repo) : base(repo)
 
114
                {
 
115
                }
 
116
 
 
117
                /// <summary>
 
118
                /// Set the stash reference to apply
 
119
                /// <p>
 
120
                /// This will default to apply the latest stashed commit (stash@{0}) if
 
121
                /// unspecified
 
122
                /// </summary>
 
123
                /// <param name="stashRef"></param>
 
124
                /// <returns>
 
125
                /// 
 
126
                /// <code>this</code>
 
127
                /// </returns>
 
128
                public virtual StashApplyCommand SetStashRef(string stashRef)
 
129
                {
 
130
                        this.stashRef = stashRef;
 
131
                        return this;
 
132
                }
 
133
 
 
134
                private bool IsEqualEntry(AbstractTreeIterator iter1, AbstractTreeIterator iter2)
 
135
                {
 
136
                        if (!iter1.EntryFileMode.Equals(iter2.EntryFileMode))
 
137
                        {
 
138
                                return false;
 
139
                        }
 
140
                        ObjectId id1 = iter1.EntryObjectId;
 
141
                        ObjectId id2 = iter2.EntryObjectId;
 
142
                        return id1 != null ? id1.Equals(id2) : id2 == null;
 
143
                }
 
144
 
 
145
                /// <summary>Would unstashing overwrite local changes?</summary>
 
146
                /// <param name="stashIndexIter"></param>
 
147
                /// <param name="stashWorkingTreeIter"></param>
 
148
                /// <param name="headIter"></param>
 
149
                /// <param name="indexIter"></param>
 
150
                /// <param name="workingTreeIter"></param>
 
151
                /// <returns>true if unstash conflict, false otherwise</returns>
 
152
                private bool IsConflict(AbstractTreeIterator stashIndexIter, AbstractTreeIterator
 
153
                         stashWorkingTreeIter, AbstractTreeIterator headIter, AbstractTreeIterator indexIter
 
154
                        , AbstractTreeIterator workingTreeIter)
 
155
                {
 
156
                        // Is the current index dirty?
 
157
                        bool indexDirty = indexIter != null && (headIter == null || !IsEqualEntry(indexIter
 
158
                                , headIter));
 
159
                        // Is the current working tree dirty?
 
160
                        bool workingTreeDirty = workingTreeIter != null && (headIter == null || !IsEqualEntry
 
161
                                (workingTreeIter, headIter));
 
162
                        // Would unstashing overwrite existing index changes?
 
163
                        if (indexDirty && stashIndexIter != null && indexIter != null && !IsEqualEntry(stashIndexIter
 
164
                                , indexIter))
 
165
                        {
 
166
                                return true;
 
167
                        }
 
168
                        // Would unstashing overwrite existing working tree changes?
 
169
                        if (workingTreeDirty && stashWorkingTreeIter != null && workingTreeIter != null &&
 
170
                                 !IsEqualEntry(stashWorkingTreeIter, workingTreeIter))
 
171
                        {
 
172
                                return true;
 
173
                        }
 
174
                        return false;
 
175
                }
 
176
 
 
177
                /// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
 
178
                /// <exception cref="NGit.Api.Errors.GitAPIException"></exception>
 
179
                private ObjectId GetHeadTree()
 
180
                {
 
181
                        ObjectId headTree;
 
182
                        try
 
183
                        {
 
184
                                headTree = repo.Resolve(Constants.HEAD + "^{tree}");
 
185
                        }
 
186
                        catch (IOException e)
 
187
                        {
 
188
                                throw new JGitInternalException(JGitText.Get().cannotReadTree, e);
 
189
                        }
 
190
                        if (headTree == null)
 
191
                        {
 
192
                                throw new NoHeadException(JGitText.Get().cannotReadTree);
 
193
                        }
 
194
                        return headTree;
 
195
                }
 
196
 
 
197
                /// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
 
198
                /// <exception cref="NGit.Api.Errors.GitAPIException"></exception>
 
199
                private ObjectId GetStashId()
 
200
                {
 
201
                        string revision = stashRef != null ? stashRef : DEFAULT_REF;
 
202
                        ObjectId stashId;
 
203
                        try
 
204
                        {
 
205
                                stashId = repo.Resolve(revision);
 
206
                        }
 
207
                        catch (IOException e)
 
208
                        {
 
209
                                throw new InvalidRefNameException(MessageFormat.Format(JGitText.Get().stashResolveFailed
 
210
                                        , revision), e);
 
211
                        }
 
212
                        if (stashId == null)
 
213
                        {
 
214
                                throw new InvalidRefNameException(MessageFormat.Format(JGitText.Get().stashResolveFailed
 
215
                                        , revision));
 
216
                        }
 
217
                        return stashId;
 
218
                }
 
219
 
 
220
                /// <exception cref="System.IO.IOException"></exception>
 
221
                private void ScanForConflicts(TreeWalk treeWalk)
 
222
                {
 
223
                        FilePath workingTree = repo.WorkTree;
 
224
                        while (treeWalk.Next())
 
225
                        {
 
226
                                // State of the stashed index and working directory
 
227
                                AbstractTreeIterator stashIndexIter = treeWalk.GetTree<AbstractTreeIterator>(1);
 
228
                                AbstractTreeIterator stashWorkingIter = treeWalk.GetTree<AbstractTreeIterator>(2);
 
229
                                // State of the current HEAD, index, and working directory
 
230
                                AbstractTreeIterator headIter = treeWalk.GetTree<AbstractTreeIterator>(3);
 
231
                                AbstractTreeIterator indexIter = treeWalk.GetTree<AbstractTreeIterator>(4);
 
232
                                AbstractTreeIterator workingIter = treeWalk.GetTree<AbstractTreeIterator>(5);
 
233
                                if (IsConflict(stashIndexIter, stashWorkingIter, headIter, indexIter, workingIter
 
234
                                        ))
 
235
                                {
 
236
                                        string path = treeWalk.PathString;
 
237
                                        FilePath file = new FilePath(workingTree, path);
 
238
                                        throw new NGit.Errors.CheckoutConflictException(file.GetAbsolutePath());
 
239
                                }
 
240
                        }
 
241
                }
 
242
 
 
243
                /// <exception cref="System.IO.IOException"></exception>
 
244
                private void ApplyChanges(TreeWalk treeWalk, DirCache cache, DirCacheEditor editor
 
245
                        )
 
246
                {
 
247
                        FilePath workingTree = repo.WorkTree;
 
248
                        while (treeWalk.Next())
 
249
                        {
 
250
                                string path = treeWalk.PathString;
 
251
                                FilePath file = new FilePath(workingTree, path);
 
252
                                // State of the stashed HEAD, index, and working directory
 
253
                                AbstractTreeIterator stashHeadIter = treeWalk.GetTree<AbstractTreeIterator>(0);
 
254
                                AbstractTreeIterator stashIndexIter = treeWalk.GetTree<AbstractTreeIterator>(1);
 
255
                                AbstractTreeIterator stashWorkingIter = treeWalk.GetTree<AbstractTreeIterator>(2);
 
256
                                if (stashWorkingIter != null && stashIndexIter != null)
 
257
                                {
 
258
                                        // Checkout index change
 
259
                                        DirCacheEntry entry = cache.GetEntry(path);
 
260
                                        if (entry == null)
 
261
                                        {
 
262
                                                entry = new DirCacheEntry(treeWalk.RawPath);
 
263
                                        }
 
264
                                        entry.FileMode = stashIndexIter.EntryFileMode;
 
265
                                        entry.SetObjectId(stashIndexIter.EntryObjectId);
 
266
                                        DirCacheCheckout.CheckoutEntry(repo, file, entry, treeWalk.ObjectReader);
 
267
                                        DirCacheEntry updatedEntry = entry;
 
268
                                        editor.Add(new _PathEdit_271(updatedEntry, path));
 
269
                                        // Checkout working directory change
 
270
                                        if (!stashWorkingIter.IdEqual(stashIndexIter))
 
271
                                        {
 
272
                                                entry = new DirCacheEntry(treeWalk.RawPath);
 
273
                                                entry.SetObjectId(stashWorkingIter.EntryObjectId);
 
274
                                                DirCacheCheckout.CheckoutEntry(repo, file, entry, treeWalk.ObjectReader);
 
275
                                        }
 
276
                                }
 
277
                                else
 
278
                                {
 
279
                                        if (stashIndexIter == null || (stashHeadIter != null && !stashIndexIter.IdEqual(stashHeadIter
 
280
                                                )))
 
281
                                        {
 
282
                                                editor.Add(new DirCacheEditor.DeletePath(path));
 
283
                                        }
 
284
                                        FileUtils.Delete(file, FileUtils.RETRY | FileUtils.SKIP_MISSING);
 
285
                                }
 
286
                        }
 
287
                }
 
288
 
 
289
                private sealed class _PathEdit_271 : DirCacheEditor.PathEdit
 
290
                {
 
291
                        public _PathEdit_271(DirCacheEntry updatedEntry, string baseArg1) : base(baseArg1
 
292
                                )
 
293
                        {
 
294
                                this.updatedEntry = updatedEntry;
 
295
                        }
 
296
 
 
297
                        public override void Apply(DirCacheEntry ent)
 
298
                        {
 
299
                                ent.CopyMetaData(updatedEntry);
 
300
                        }
 
301
 
 
302
                        private readonly DirCacheEntry updatedEntry;
 
303
                }
 
304
 
 
305
                /// <summary>Apply the changes in a stashed commit to the working directory and index
 
306
                ///     </summary>
 
307
                /// <returns>id of stashed commit that was applied</returns>
 
308
                /// <exception cref="NGit.Api.Errors.GitAPIException"></exception>
 
309
                /// <exception cref="NGit.Api.Errors.JGitInternalException"></exception>
 
310
                public override ObjectId Call()
 
311
                {
 
312
                        CheckCallable();
 
313
                        if (repo.GetRepositoryState() != RepositoryState.SAFE)
 
314
                        {
 
315
                                throw new WrongRepositoryStateException(MessageFormat.Format(JGitText.Get().stashApplyOnUnsafeRepository
 
316
                                        , repo.GetRepositoryState()));
 
317
                        }
 
318
                        ObjectId headTree = GetHeadTree();
 
319
                        ObjectId stashId = GetStashId();
 
320
                        ObjectReader reader = repo.NewObjectReader();
 
321
                        try
 
322
                        {
 
323
                                RevWalk revWalk = new RevWalk(reader);
 
324
                                RevCommit stashCommit = revWalk.ParseCommit(stashId);
 
325
                                if (stashCommit.ParentCount != 2)
 
326
                                {
 
327
                                        throw new JGitInternalException(MessageFormat.Format(JGitText.Get().stashCommitMissingTwoParents
 
328
                                                , stashId.Name));
 
329
                                }
 
330
                                RevTree stashWorkingTree = stashCommit.Tree;
 
331
                                RevTree stashIndexTree = revWalk.ParseCommit(stashCommit.GetParent(1)).Tree;
 
332
                                RevTree stashHeadTree = revWalk.ParseCommit(stashCommit.GetParent(0)).Tree;
 
333
                                CanonicalTreeParser stashWorkingIter = new CanonicalTreeParser();
 
334
                                stashWorkingIter.Reset(reader, stashWorkingTree);
 
335
                                CanonicalTreeParser stashIndexIter = new CanonicalTreeParser();
 
336
                                stashIndexIter.Reset(reader, stashIndexTree);
 
337
                                CanonicalTreeParser stashHeadIter = new CanonicalTreeParser();
 
338
                                stashHeadIter.Reset(reader, stashHeadTree);
 
339
                                CanonicalTreeParser headIter = new CanonicalTreeParser();
 
340
                                headIter.Reset(reader, headTree);
 
341
                                DirCache cache = repo.LockDirCache();
 
342
                                DirCacheEditor editor = cache.Editor();
 
343
                                try
 
344
                                {
 
345
                                        DirCacheIterator indexIter = new DirCacheIterator(cache);
 
346
                                        FileTreeIterator workingIter = new FileTreeIterator(repo);
 
347
                                        TreeWalk treeWalk = new TreeWalk(reader);
 
348
                                        treeWalk.Recursive = true;
 
349
                                        treeWalk.Filter = new StashApplyCommand.StashDiffFilter();
 
350
                                        treeWalk.AddTree(stashHeadIter);
 
351
                                        treeWalk.AddTree(stashIndexIter);
 
352
                                        treeWalk.AddTree(stashWorkingIter);
 
353
                                        treeWalk.AddTree(headIter);
 
354
                                        treeWalk.AddTree(indexIter);
 
355
                                        treeWalk.AddTree(workingIter);
 
356
                                        ScanForConflicts(treeWalk);
 
357
                                        // Reset trees and walk
 
358
                                        treeWalk.Reset();
 
359
                                        stashWorkingIter.Reset(reader, stashWorkingTree);
 
360
                                        stashIndexIter.Reset(reader, stashIndexTree);
 
361
                                        stashHeadIter.Reset(reader, stashHeadTree);
 
362
                                        treeWalk.AddTree(stashHeadIter);
 
363
                                        treeWalk.AddTree(stashIndexIter);
 
364
                                        treeWalk.AddTree(stashWorkingIter);
 
365
                                        ApplyChanges(treeWalk, cache, editor);
 
366
                                }
 
367
                                finally
 
368
                                {
 
369
                                        editor.Commit();
 
370
                                        cache.Unlock();
 
371
                                }
 
372
                        }
 
373
                        catch (JGitInternalException e)
 
374
                        {
 
375
                                throw;
 
376
                        }
 
377
                        catch (IOException e)
 
378
                        {
 
379
                                throw new JGitInternalException(JGitText.Get().stashApplyFailed, e);
 
380
                        }
 
381
                        finally
 
382
                        {
 
383
                                reader.Release();
 
384
                        }
 
385
                        return stashId;
 
386
                }
 
387
        }
 
388
}