|
@@ -0,0 +1,119 @@
|
|
|
+# stash.py
|
|
|
+# Copyright (C) 2018 Jelmer Vernooij <jelmer@samba.org>
|
|
|
+#
|
|
|
+# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
|
|
|
+# General Public License as public by the Free Software Foundation; version 2.0
|
|
|
+# or (at your option) any later version. You can redistribute it and/or
|
|
|
+# modify it under the terms of either of these two licenses.
|
|
|
+#
|
|
|
+# Unless required by applicable law or agreed to in writing, software
|
|
|
+# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+# See the License for the specific language governing permissions and
|
|
|
+# limitations under the License.
|
|
|
+#
|
|
|
+# You should have received a copy of the licenses; if not, see
|
|
|
+# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
|
|
|
+# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
|
|
|
+# License, Version 2.0.
|
|
|
+#
|
|
|
+
|
|
|
+"""Stash handling."""
|
|
|
+
|
|
|
+from __future__ import absolute_import
|
|
|
+
|
|
|
+import errno
|
|
|
+import os
|
|
|
+
|
|
|
+from dulwich.file import GitFile
|
|
|
+from dulwich.index import (
|
|
|
+ commit_tree,
|
|
|
+ iter_fresh_objects,
|
|
|
+ )
|
|
|
+from dulwich.reflog import read_reflog
|
|
|
+
|
|
|
+
|
|
|
+DEFAULT_STASH_REF = b"refs/stash"
|
|
|
+
|
|
|
+
|
|
|
+class Stash(object):
|
|
|
+ """A Git stash.
|
|
|
+
|
|
|
+ Note that this doesn't currently update the working tree.
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, repo, ref=DEFAULT_STASH_REF):
|
|
|
+ self._ref = ref
|
|
|
+ self._repo = repo
|
|
|
+
|
|
|
+ def stashes(self):
|
|
|
+ reflog_path = os.path.join(
|
|
|
+ self._repo.commondir(), 'logs', self._ref)
|
|
|
+ try:
|
|
|
+ with GitFile(reflog_path, 'rb') as f:
|
|
|
+ return reversed(list(read_reflog(f)))
|
|
|
+ except EnvironmentError as e:
|
|
|
+ if e.errno == errno.ENOENT:
|
|
|
+ return []
|
|
|
+ raise
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def from_repo(cls, repo):
|
|
|
+ """Create a new stash from a Repo object."""
|
|
|
+ return cls(repo)
|
|
|
+
|
|
|
+ def drop(self, index):
|
|
|
+ """Drop entry with specified index."""
|
|
|
+ raise NotImplementedError(self.drop)
|
|
|
+
|
|
|
+ def pop(self, index):
|
|
|
+ raise NotImplementedError(self.drop)
|
|
|
+
|
|
|
+ def push(self, committer=None, author=None, message=None):
|
|
|
+ """Create a new stash.
|
|
|
+
|
|
|
+ :param committer: Optional committer name to use
|
|
|
+ :param author: Optional author name to use
|
|
|
+ :param message: Optional commit message
|
|
|
+ """
|
|
|
+ # First, create the index commit.
|
|
|
+ commit_kwargs = {}
|
|
|
+ if committer is not None:
|
|
|
+ commit_kwargs['committer'] = committer
|
|
|
+ if author is not None:
|
|
|
+ commit_kwargs['author'] = author
|
|
|
+
|
|
|
+ index = self._repo.open_index()
|
|
|
+ index_tree_id = index.commit(self._repo.object_store)
|
|
|
+ index_commit_id = self._repo.do_commit(
|
|
|
+ ref=None, tree=index_tree_id,
|
|
|
+ message=b"Index stash",
|
|
|
+ merge_heads=[self._repo.head()],
|
|
|
+ **commit_kwargs)
|
|
|
+
|
|
|
+ # Then, the working tree one.
|
|
|
+ stash_tree_id = commit_tree(
|
|
|
+ self._repo.object_store,
|
|
|
+ iter_fresh_objects(
|
|
|
+ index, self._repo.path,
|
|
|
+ object_store=self._repo.object_store))
|
|
|
+
|
|
|
+ if message is None:
|
|
|
+ message = b"A stash on " + self._repo.head()
|
|
|
+
|
|
|
+ # TODO(jelmer): Just pass parents into do_commit()?
|
|
|
+ self._repo.refs[self._ref] = self._repo.head()
|
|
|
+
|
|
|
+ cid = self._repo.do_commit(
|
|
|
+ ref=self._ref, tree=stash_tree_id,
|
|
|
+ message=message,
|
|
|
+ merge_heads=[index_commit_id],
|
|
|
+ **commit_kwargs)
|
|
|
+
|
|
|
+ return cid
|
|
|
+
|
|
|
+ def __getitem__(self, index):
|
|
|
+ return self._stashes()[index]
|
|
|
+
|
|
|
+ def __len__(self):
|
|
|
+ return len(self._stashes())
|